@cornerstonejs/tools 2.5.3 → 2.6.0

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 (26) hide show
  1. package/dist/esm/enums/StrategyCallbacks.d.ts +1 -0
  2. package/dist/esm/enums/StrategyCallbacks.js +1 -0
  3. package/dist/esm/tools/annotation/DragProbeTool.js +2 -1
  4. package/dist/esm/tools/annotation/LivewireContourTool.js +1 -1
  5. package/dist/esm/tools/annotation/PlanarFreehandROITool.d.ts +3 -3
  6. package/dist/esm/tools/annotation/ProbeTool.d.ts +13 -3
  7. package/dist/esm/tools/annotation/ProbeTool.js +10 -9
  8. package/dist/esm/tools/annotation/SplineROITool.d.ts +2 -2
  9. package/dist/esm/tools/base/AnnotationDisplayTool.d.ts +2 -1
  10. package/dist/esm/tools/base/AnnotationDisplayTool.js +40 -1
  11. package/dist/esm/tools/base/BaseTool.d.ts +10 -0
  12. package/dist/esm/tools/base/BaseTool.js +20 -8
  13. package/dist/esm/tools/base/ContourBaseTool.d.ts +1 -1
  14. package/dist/esm/tools/base/ContourBaseTool.js +9 -39
  15. package/dist/esm/tools/base/ContourSegmentationBaseTool.d.ts +2 -1
  16. package/dist/esm/tools/base/ContourSegmentationBaseTool.js +0 -1
  17. package/dist/esm/tools/displayTools/Labelmap/addLabelmapToElement.js +1 -1
  18. package/dist/esm/tools/segmentation/BrushTool.d.ts +3 -108
  19. package/dist/esm/tools/segmentation/BrushTool.js +4 -194
  20. package/dist/esm/tools/segmentation/LabelmapBaseTool.d.ts +155 -0
  21. package/dist/esm/tools/segmentation/LabelmapBaseTool.js +218 -0
  22. package/dist/esm/tools/segmentation/strategies/BrushStrategy.d.ts +2 -0
  23. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js +8 -0
  24. package/dist/esm/tools/segmentation/strategies/compositions/determineSegmentIndex.js +14 -1
  25. package/dist/esm/types/AnnotationTypes.d.ts +2 -1
  26. package/package.json +3 -3
@@ -9,6 +9,7 @@ declare enum StrategyCallbacks {
9
9
  CreateIsInThreshold = "createIsInThreshold",
10
10
  Initialize = "initialize",
11
11
  INTERNAL_setValue = "setValue",
12
+ AddPreview = "addPreview",
12
13
  ComputeInnerCircleRadius = "computeInnerCircleRadius",
13
14
  GetStatistics = "getStatistics"
14
15
  }
@@ -10,6 +10,7 @@ var StrategyCallbacks;
10
10
  StrategyCallbacks["CreateIsInThreshold"] = "createIsInThreshold";
11
11
  StrategyCallbacks["Initialize"] = "initialize";
12
12
  StrategyCallbacks["INTERNAL_setValue"] = "setValue";
13
+ StrategyCallbacks["AddPreview"] = "addPreview";
13
14
  StrategyCallbacks["ComputeInnerCircleRadius"] = "computeInnerCircleRadius";
14
15
  StrategyCallbacks["GetStatistics"] = "getStatistics";
15
16
  })(StrategyCallbacks || (StrategyCallbacks = {}));
@@ -2,6 +2,7 @@ import { getEnabledElement } from '@cornerstonejs/core';
2
2
  import { drawHandles as drawHandlesSvg, drawTextBox as drawTextBoxSvg, } from '../../drawingSvg';
3
3
  import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
4
4
  import { hideElementCursor } from '../../cursors/elementCursor';
5
+ import { ChangeTypes, Events } from '../../enums';
5
6
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
6
7
  import ProbeTool from './ProbeTool';
7
8
  class DragProbeTool extends ProbeTool {
@@ -84,7 +85,7 @@ class DragProbeTool extends ProbeTool {
84
85
  styleSpecifier,
85
86
  });
86
87
  if (!data.cachedStats[targetId] ||
87
- data.cachedStats[targetId].value == null) {
88
+ data.cachedStats[targetId].value === null) {
88
89
  data.cachedStats[targetId] = {
89
90
  Modality: null,
90
91
  index: null,
@@ -605,7 +605,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
605
605
  }
606
606
  super.renderAnnotationInstance(renderContext);
607
607
  if (!data.cachedStats[targetId] ||
608
- data.cachedStats[targetId].areaUnit == null) {
608
+ data.cachedStats[targetId]?.areaUnit === null) {
609
609
  data.cachedStats[targetId] = {
610
610
  Modality: null,
611
611
  area: null,
@@ -1,6 +1,6 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
- import type { EventTypes, ToolHandle, Annotation, Annotations, PublicToolProps, ToolProps, AnnotationRenderContext } from '../../types';
3
- import type { PlanarFreehandROIAnnotation } from '../../types/ToolSpecificAnnotationTypes';
2
+ import type { EventTypes, ToolHandle, Annotations, PublicToolProps, ToolProps, AnnotationRenderContext } from '../../types';
3
+ import type { ContourAnnotation, PlanarFreehandROIAnnotation } from '../../types/ToolSpecificAnnotationTypes';
4
4
  import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
5
5
  declare class PlanarFreehandROITool extends ContourSegmentationBaseTool {
6
6
  static toolName: any;
@@ -30,7 +30,7 @@ declare class PlanarFreehandROITool extends ContourSegmentationBaseTool {
30
30
  filterInteractableAnnotationsForElement(element: HTMLDivElement, annotations: Annotations): Annotations | undefined;
31
31
  private filterAnnotationsWithinSlice;
32
32
  protected isContourSegmentationTool(): boolean;
33
- protected createAnnotation(evt: EventTypes.InteractionEventType): Annotation;
33
+ protected createAnnotation(evt: EventTypes.InteractionEventType): ContourAnnotation;
34
34
  protected getAnnotationStyle(context: any): any;
35
35
  protected renderAnnotationInstance(renderContext: AnnotationRenderContext): boolean;
36
36
  _calculateStatsIfActive(annotation: PlanarFreehandROIAnnotation, targetId: string, viewport: any, renderingEngine: any, enabledElement: any): void;
@@ -1,6 +1,7 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import { AnnotationTool } from '../base';
3
- import type { EventTypes, ToolHandle, PublicToolProps, ToolProps, SVGDrawingHelper, Annotation } from '../../types';
3
+ import { ChangeTypes } from '../../enums';
4
+ import type { EventTypes, ToolHandle, PublicToolProps, SVGDrawingHelper, Annotation } from '../../types';
4
5
  import type { ProbeAnnotation } from '../../types/ToolSpecificAnnotationTypes';
5
6
  declare class ProbeTool extends AnnotationTool {
6
7
  static toolName: any;
@@ -15,7 +16,15 @@ declare class ProbeTool extends AnnotationTool {
15
16
  };
16
17
  isDrawing: boolean;
17
18
  isHandleOutsideImage: boolean;
18
- constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
19
+ static probeDefaults: {
20
+ supportedInteractionTypes: string[];
21
+ configuration: {
22
+ shadow: boolean;
23
+ preventHandleOutsideImage: boolean;
24
+ getTextLines: typeof defaultGetTextLines;
25
+ };
26
+ };
27
+ constructor(toolProps?: PublicToolProps, defaultToolProps?: any);
19
28
  isPointNearTool(): boolean;
20
29
  toolSelectedCallback(): void;
21
30
  static hydrate: (viewportId: string, points: Types.Point3[], options?: {
@@ -30,6 +39,7 @@ declare class ProbeTool extends AnnotationTool {
30
39
  _activateModify: (element: any) => void;
31
40
  _deactivateModify: (element: any) => void;
32
41
  renderAnnotation: (enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper) => boolean;
33
- _calculateCachedStats(annotation: any, renderingEngine: any, enabledElement: any): any;
42
+ _calculateCachedStats(annotation: any, renderingEngine: any, enabledElement: any, changeType?: ChangeTypes): any;
34
43
  }
44
+ declare function defaultGetTextLines(data: any, targetId: any): string[];
35
45
  export default ProbeTool;
@@ -6,7 +6,7 @@ import { triggerAnnotationCompleted, triggerAnnotationModified, } from '../../st
6
6
  import { getCalibratedProbeUnitsAndValue } from '../../utilities/getCalibratedUnits';
7
7
  import { drawHandles as drawHandlesSvg, drawTextBox as drawTextBoxSvg, } from '../../drawingSvg';
8
8
  import { state } from '../../store/state';
9
- import { Events } from '../../enums';
9
+ import { ChangeTypes, Events } from '../../enums';
10
10
  import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
11
11
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
12
12
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
@@ -14,15 +14,16 @@ import { getPixelValueUnits } from '../../utilities/getPixelValueUnits';
14
14
  import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';
15
15
  const { transformWorldToIndex } = csUtils;
16
16
  class ProbeTool extends AnnotationTool {
17
- constructor(toolProps = {}, defaultToolProps = {
17
+ static { this.probeDefaults = {
18
18
  supportedInteractionTypes: ['Mouse', 'Touch'],
19
19
  configuration: {
20
20
  shadow: true,
21
21
  preventHandleOutsideImage: false,
22
22
  getTextLines: defaultGetTextLines,
23
23
  },
24
- }) {
25
- super(toolProps, defaultToolProps);
24
+ }; }
25
+ constructor(toolProps = {}, defaultToolProps) {
26
+ super(toolProps, AnnotationTool.mergeDefaultProps(ProbeTool.probeDefaults, defaultToolProps));
26
27
  this.addNewAnnotation = (evt) => {
27
28
  const eventDetail = evt.detail;
28
29
  const { currentPoints, element } = eventDetail;
@@ -164,13 +165,13 @@ class ProbeTool extends AnnotationTool {
164
165
  data.cachedStats = {};
165
166
  }
166
167
  if (!data.cachedStats[targetId] ||
167
- data.cachedStats[targetId].value == null) {
168
+ data.cachedStats[targetId].value === null) {
168
169
  data.cachedStats[targetId] = {
169
170
  Modality: null,
170
171
  index: null,
171
172
  value: null,
172
173
  };
173
- this._calculateCachedStats(annotation, renderingEngine, enabledElement);
174
+ this._calculateCachedStats(annotation, renderingEngine, enabledElement, ChangeTypes.StatsUpdated);
174
175
  }
175
176
  else if (annotation.invalidated) {
176
177
  this._calculateCachedStats(annotation, renderingEngine, enabledElement);
@@ -280,7 +281,7 @@ class ProbeTool extends AnnotationTool {
280
281
  triggerAnnotationRenderForViewportIds(viewportIdsToRender);
281
282
  evt.preventDefault();
282
283
  }
283
- _calculateCachedStats(annotation, renderingEngine, enabledElement) {
284
+ _calculateCachedStats(annotation, renderingEngine, enabledElement, changeType = ChangeTypes.StatsUpdated) {
284
285
  const data = annotation.data;
285
286
  const { renderingEngineId, viewport } = enabledElement;
286
287
  const { element } = viewport;
@@ -340,7 +341,7 @@ class ProbeTool extends AnnotationTool {
340
341
  };
341
342
  }
342
343
  annotation.invalidated = false;
343
- triggerAnnotationModified(annotation, element);
344
+ triggerAnnotationModified(annotation, element, changeType);
344
345
  }
345
346
  return cachedStats;
346
347
  }
@@ -348,7 +349,7 @@ class ProbeTool extends AnnotationTool {
348
349
  function defaultGetTextLines(data, targetId) {
349
350
  const cachedVolumeStats = data.cachedStats[targetId];
350
351
  const { index, value, modalityUnit } = cachedVolumeStats;
351
- if (value === undefined) {
352
+ if (value === undefined || !index) {
352
353
  return;
353
354
  }
354
355
  const textLines = [];
@@ -1,7 +1,7 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import { ChangeTypes } from '../../enums';
3
3
  import type { Annotation, EventTypes, ToolHandle, PublicToolProps, ToolProps, AnnotationRenderContext } from '../../types';
4
- import type { SplineROIAnnotation } from '../../types/ToolSpecificAnnotationTypes';
4
+ import type { ContourAnnotation, SplineROIAnnotation } from '../../types/ToolSpecificAnnotationTypes';
5
5
  import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
6
6
  declare enum SplineTypesEnum {
7
7
  Cardinal = "CARDINAL",
@@ -56,7 +56,7 @@ declare class SplineROITool extends ContourSegmentationBaseTool {
56
56
  protected isContourSegmentationTool(): boolean;
57
57
  protected renderAnnotationInstance(renderContext: AnnotationRenderContext): boolean;
58
58
  protected createInterpolatedSplineControl(annotation: any): void;
59
- protected createAnnotation(evt: EventTypes.InteractionEventType): Annotation;
59
+ protected createAnnotation(evt: EventTypes.InteractionEventType): ContourAnnotation;
60
60
  private _renderStats;
61
61
  addControlPointCallback: (evt: EventTypes.InteractionEventType, annotation: SplineROIAnnotation) => void;
62
62
  private _deleteControlPointByIndex;
@@ -1,12 +1,13 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import BaseTool from './BaseTool';
3
- import type { Annotation, Annotations, SVGDrawingHelper } from '../../types';
3
+ import type { Annotation, Annotations, EventTypes, SVGDrawingHelper } from '../../types';
4
4
  import type { StyleSpecifier } from '../../types/AnnotationStyle';
5
5
  declare abstract class AnnotationDisplayTool extends BaseTool {
6
6
  static toolName: any;
7
7
  abstract renderAnnotation(enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper): any;
8
8
  filterInteractableAnnotationsForElement(element: HTMLDivElement, annotations: Annotations): Annotations | undefined;
9
9
  onImageSpacingCalibrated: (evt: Types.EventTypes.ImageSpacingCalibratedEvent) => void;
10
+ protected createAnnotation(evt: EventTypes.InteractionEventType): Annotation;
10
11
  protected getReferencedImageId(viewport: Types.IViewport, worldPos: Types.Point3, viewPlaneNormal: Types.Point3, viewUp?: Types.Point3): string;
11
12
  getStyle(property: string, specifications: StyleSpecifier, annotation?: Annotation): unknown;
12
13
  }
@@ -1,4 +1,4 @@
1
- import { utilities, getEnabledElement, StackViewport, cache, VideoViewport, BaseVolumeViewport, } from '@cornerstonejs/core';
1
+ import { utilities, getEnabledElement, cache, BaseVolumeViewport, } from '@cornerstonejs/core';
2
2
  import BaseTool from './BaseTool';
3
3
  import { getAnnotationManager } from '../../stateManagement/annotation/annotationState';
4
4
  import triggerAnnotationRender from '../../utilities/triggerAnnotationRender';
@@ -41,6 +41,45 @@ class AnnotationDisplayTool extends BaseTool {
41
41
  const { viewport } = enabledElement;
42
42
  return filterAnnotationsForDisplay(viewport, annotations);
43
43
  }
44
+ createAnnotation(evt) {
45
+ const eventDetail = evt.detail;
46
+ const { currentPoints, element } = eventDetail;
47
+ const { world: worldPos } = currentPoints;
48
+ const enabledElement = getEnabledElement(element);
49
+ const { viewport } = enabledElement;
50
+ const camera = viewport.getCamera();
51
+ const { viewPlaneNormal, viewUp, position: cameraPosition } = camera;
52
+ const referencedImageId = this.getReferencedImageId(viewport, worldPos, viewPlaneNormal, viewUp);
53
+ const viewReference = viewport.getViewReference({ points: [worldPos] });
54
+ return {
55
+ highlighted: true,
56
+ invalidated: true,
57
+ metadata: {
58
+ toolName: this.getToolName(),
59
+ ...viewReference,
60
+ referencedImageId,
61
+ viewUp,
62
+ cameraPosition,
63
+ },
64
+ data: {
65
+ cachedStats: {},
66
+ handles: {
67
+ points: [],
68
+ activeHandleIndex: null,
69
+ textBox: {
70
+ hasMoved: false,
71
+ worldPosition: [0, 0, 0],
72
+ worldBoundingBox: {
73
+ topLeft: [0, 0, 0],
74
+ topRight: [0, 0, 0],
75
+ bottomLeft: [0, 0, 0],
76
+ bottomRight: [0, 0, 0],
77
+ },
78
+ },
79
+ },
80
+ },
81
+ };
82
+ }
44
83
  getReferencedImageId(viewport, worldPos, viewPlaneNormal, viewUp) {
45
84
  const targetId = this.getTargetId(viewport);
46
85
  let referencedImageId = targetId.split(/^[a-zA-Z]+:/)[1];
@@ -10,7 +10,17 @@ declare abstract class BaseTool {
10
10
  toolGroupId: string;
11
11
  mode: ToolModes;
12
12
  protected memo: utilities.HistoryMemo.Memo;
13
+ static defaults: {
14
+ configuration: {
15
+ strategies: {};
16
+ defaultStrategy: any;
17
+ activeStrategy: any;
18
+ strategyOptions: {};
19
+ };
20
+ };
13
21
  constructor(toolProps: PublicToolProps, defaultToolProps: ToolProps);
22
+ static mergeDefaultProps(defaultProps?: {}, additionalProps?: any): any;
23
+ get toolName(): string;
14
24
  getToolName(): string;
15
25
  applyActiveStrategy(enabledElement: Types.IEnabledElement, operationData: unknown): any;
16
26
  applyActiveStrategyCallback(enabledElement: Types.IEnabledElement, operationData: unknown, callbackType: StrategyCallbacks | string, ...extraArgs: any[]): any;
@@ -1,21 +1,33 @@
1
- import { utilities, BaseVolumeViewport } from '@cornerstonejs/core';
1
+ import { utilities } from '@cornerstonejs/core';
2
2
  import ToolModes from '../../enums/ToolModes';
3
3
  const { DefaultHistoryMemo } = utilities.HistoryMemo;
4
4
  class BaseTool {
5
+ static { this.defaults = {
6
+ configuration: {
7
+ strategies: {},
8
+ defaultStrategy: undefined,
9
+ activeStrategy: undefined,
10
+ strategyOptions: {},
11
+ },
12
+ }; }
5
13
  constructor(toolProps, defaultToolProps) {
6
- const initialProps = utilities.deepMerge(defaultToolProps, toolProps);
14
+ const mergedDefaults = BaseTool.mergeDefaultProps(BaseTool.defaults, defaultToolProps);
15
+ const initialProps = utilities.deepMerge(mergedDefaults, toolProps);
7
16
  const { configuration = {}, supportedInteractionTypes, toolGroupId, } = initialProps;
8
- if (!configuration.strategies) {
9
- configuration.strategies = {};
10
- configuration.defaultStrategy = undefined;
11
- configuration.activeStrategy = undefined;
12
- configuration.strategyOptions = {};
13
- }
14
17
  this.toolGroupId = toolGroupId;
15
18
  this.supportedInteractionTypes = supportedInteractionTypes || [];
16
19
  this.configuration = Object.assign({}, configuration);
17
20
  this.mode = ToolModes.Disabled;
18
21
  }
22
+ static mergeDefaultProps(defaultProps = {}, additionalProps) {
23
+ if (!additionalProps) {
24
+ return defaultProps;
25
+ }
26
+ return utilities.deepMerge(defaultProps, additionalProps);
27
+ }
28
+ get toolName() {
29
+ return this.getToolName();
30
+ }
19
31
  getToolName() {
20
32
  return this.constructor.toolName;
21
33
  }
@@ -5,7 +5,7 @@ import type { ContourWindingDirection } from '../../types/ContourAnnotation';
5
5
  declare abstract class ContourBaseTool extends AnnotationTool {
6
6
  constructor(toolProps: PublicToolProps, defaultToolProps: ToolProps);
7
7
  renderAnnotation(enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper): boolean;
8
- protected createAnnotation(evt: EventTypes.InteractionEventType): Annotation;
8
+ protected createAnnotation(evt: EventTypes.InteractionEventType): ContourAnnotation;
9
9
  protected addAnnotation(annotation: Annotation, element: HTMLDivElement): string;
10
10
  protected cancelAnnotation(annotation: Annotation): void;
11
11
  protected moveAnnotation(annotation: Annotation, worldPosDelta: Types.Point3): void;
@@ -53,48 +53,18 @@ class ContourBaseTool extends AnnotationTool {
53
53
  return renderStatus;
54
54
  }
55
55
  createAnnotation(evt) {
56
- const eventDetail = evt.detail;
57
- const { currentPoints, element } = eventDetail;
58
- const { world: worldPos } = currentPoints;
59
- const enabledElement = getEnabledElement(element);
60
- const { viewport } = enabledElement;
61
- const camera = viewport.getCamera();
62
- const { viewPlaneNormal, viewUp, position: cameraPosition } = camera;
63
- const referencedImageId = this.getReferencedImageId(viewport, worldPos, viewPlaneNormal, viewUp);
64
- const viewReference = viewport.getViewReference({ points: [worldPos] });
65
- return {
66
- highlighted: true,
67
- invalidated: true,
68
- metadata: {
69
- toolName: this.getToolName(),
70
- ...viewReference,
71
- referencedImageId,
72
- viewUp,
73
- cameraPosition,
74
- },
75
- data: {
76
- handles: {
77
- points: [],
78
- activeHandleIndex: null,
79
- textBox: {
80
- hasMoved: false,
81
- worldPosition: [0, 0, 0],
82
- worldBoundingBox: {
83
- topLeft: [0, 0, 0],
84
- topRight: [0, 0, 0],
85
- bottomLeft: [0, 0, 0],
86
- bottomRight: [0, 0, 0],
87
- },
88
- },
89
- },
90
- contour: {
91
- polyline: [],
92
- closed: false,
93
- },
56
+ const annotation = super.createAnnotation(evt);
57
+ Object.assign(annotation.data, {
58
+ contour: {
59
+ polyline: [],
60
+ closed: false,
94
61
  },
62
+ });
63
+ Object.assign(annotation, {
95
64
  interpolationUID: '',
96
65
  autoGenerated: false,
97
- };
66
+ });
67
+ return annotation;
98
68
  }
99
69
  addAnnotation(annotation, element) {
100
70
  return addAnnotation(annotation, element);
@@ -1,10 +1,11 @@
1
1
  import type { Annotation, EventTypes, PublicToolProps, ToolProps, AnnotationRenderContext } from '../../types';
2
+ import type { ContourAnnotation } from '../../types/ToolSpecificAnnotationTypes';
2
3
  import type { StyleSpecifier } from '../../types/AnnotationStyle';
3
4
  import ContourBaseTool from './ContourBaseTool';
4
5
  declare abstract class ContourSegmentationBaseTool extends ContourBaseTool {
5
6
  constructor(toolProps: PublicToolProps, defaultToolProps: ToolProps);
6
7
  protected isContourSegmentationTool(): boolean;
7
- protected createAnnotation(evt: EventTypes.InteractionEventType): Annotation;
8
+ protected createAnnotation(evt: EventTypes.InteractionEventType): ContourAnnotation;
8
9
  protected addAnnotation(annotation: Annotation, element: HTMLDivElement): string;
9
10
  protected cancelAnnotation(annotation: Annotation): void;
10
11
  protected getAnnotationStyle(context: {
@@ -1,5 +1,4 @@
1
1
  import { getEnabledElement, utilities } from '@cornerstonejs/core';
2
- import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
3
2
  import { SegmentationRepresentations } from '../../enums';
4
3
  import ContourBaseTool from './ContourBaseTool';
5
4
  import { triggerSegmentationDataModified } from '../../stateManagement/segmentation/triggerSegmentationEvents';
@@ -35,7 +35,7 @@ async function addLabelmapToElement(element, labelMapData, segmentationId) {
35
35
  representationUID: `${segmentationId}-${SegmentationRepresentations.Labelmap}`,
36
36
  },
37
37
  ];
38
- await addImageSlicesToViewports(renderingEngine, stackInputs, [viewportId]);
38
+ addImageSlicesToViewports(renderingEngine, stackInputs, [viewportId]);
39
39
  }
40
40
  triggerSegmentationDataModified(segmentationId);
41
41
  }
@@ -1,127 +1,22 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import type { PublicToolProps, ToolProps, EventTypes, SVGDrawingHelper } from '../../types';
3
- import { BaseTool } from '../base';
4
- import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
5
- export type PreviewData = {
6
- preview: unknown;
7
- timer?: number;
8
- timerStart: number;
9
- startPoint: Types.Point2;
10
- element: HTMLDivElement;
11
- isDrag: boolean;
12
- };
13
- declare class BrushTool extends BaseTool {
3
+ import LabelmapBaseTool from './LabelmapBaseTool';
4
+ declare class BrushTool extends LabelmapBaseTool {
14
5
  static toolName: any;
15
- private _editData;
16
- private _hoverData?;
17
- private _previewData?;
6
+ prg: any;
18
7
  constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
19
8
  onSetToolPassive: (evt: any) => void;
20
9
  onSetToolEnabled: () => void;
21
10
  onSetToolDisabled: (evt: any) => void;
22
11
  private disableCursor;
23
- createEditData(element: any): {
24
- volumeId: string;
25
- referencedVolumeId: any;
26
- segmentsLocked: number[] | [];
27
- imageId?: undefined;
28
- override?: undefined;
29
- } | {
30
- imageId: string;
31
- segmentsLocked: number[] | [];
32
- override: {
33
- voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
34
- imageData: vtkImageData;
35
- };
36
- volumeId?: undefined;
37
- referencedVolumeId?: undefined;
38
- } | {
39
- imageId: string;
40
- segmentsLocked: number[] | [];
41
- volumeId?: undefined;
42
- referencedVolumeId?: undefined;
43
- override?: undefined;
44
- };
45
12
  preMouseDownCallback: (evt: EventTypes.MouseDownActivateEventType) => boolean;
46
13
  mouseMoveCallback: (evt: EventTypes.InteractionEventType) => void;
47
14
  previewCallback: () => void;
48
- private createHoverData;
49
- private getActiveSegmentationData;
50
15
  protected updateCursor(evt: EventTypes.InteractionEventType): void;
51
16
  private _dragCallback;
52
- protected getOperationData(element?: any): {
53
- points: any;
54
- segmentIndex: number;
55
- previewColors: any;
56
- viewPlaneNormal: any;
57
- toolGroupId: string;
58
- segmentationId: string;
59
- viewUp: any;
60
- strategySpecificConfiguration: any;
61
- preview: unknown;
62
- override: {
63
- voxelManager: Types.IVoxelManager<number>;
64
- imageData: vtkImageData;
65
- };
66
- segmentsLocked: number[];
67
- imageId?: string;
68
- imageIds?: string[];
69
- volumeId?: string;
70
- referencedVolumeId?: string;
71
- } | {
72
- points: any;
73
- segmentIndex: number;
74
- previewColors: any;
75
- viewPlaneNormal: any;
76
- toolGroupId: string;
77
- segmentationId: string;
78
- viewUp: any;
79
- strategySpecificConfiguration: any;
80
- preview: unknown;
81
- volumeId: string;
82
- referencedVolumeId: any;
83
- segmentsLocked: number[] | [];
84
- imageId?: undefined;
85
- override?: undefined;
86
- } | {
87
- points: any;
88
- segmentIndex: number;
89
- previewColors: any;
90
- viewPlaneNormal: any;
91
- toolGroupId: string;
92
- segmentationId: string;
93
- viewUp: any;
94
- strategySpecificConfiguration: any;
95
- preview: unknown;
96
- imageId: string;
97
- segmentsLocked: number[] | [];
98
- override: {
99
- voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
100
- imageData: vtkImageData;
101
- };
102
- volumeId?: undefined;
103
- referencedVolumeId?: undefined;
104
- } | {
105
- points: any;
106
- segmentIndex: number;
107
- previewColors: any;
108
- viewPlaneNormal: any;
109
- toolGroupId: string;
110
- segmentationId: string;
111
- viewUp: any;
112
- strategySpecificConfiguration: any;
113
- preview: unknown;
114
- imageId: string;
115
- segmentsLocked: number[] | [];
116
- volumeId?: undefined;
117
- referencedVolumeId?: undefined;
118
- override?: undefined;
119
- };
120
17
  private _calculateCursor;
121
18
  private _endCallback;
122
19
  getStatistics(element: any, segmentIndices?: any): any;
123
- rejectPreview(element?: HTMLDivElement): void;
124
- acceptPreview(element?: HTMLDivElement): void;
125
20
  private _activateDraw;
126
21
  private _deactivateDraw;
127
22
  invalidateBrushCursor(): void;
@@ -1,20 +1,15 @@
1
- import { utilities as csUtils, cache, getEnabledElement, StackViewport, eventTarget, Enums, BaseVolumeViewport, volumeLoader, } from '@cornerstonejs/core';
1
+ import { getEnabledElement } from '@cornerstonejs/core';
2
2
  import { vec3, vec2 } from 'gl-matrix';
3
- import { BaseTool } from '../base';
4
3
  import { fillInsideSphere, thresholdInsideSphere, thresholdInsideSphereIsland, } from './strategies/fillSphere';
5
4
  import { eraseInsideSphere } from './strategies/eraseSphere';
6
5
  import { thresholdInsideCircle, fillInsideCircle, } from './strategies/fillCircle';
7
6
  import { eraseInsideCircle } from './strategies/eraseCircle';
8
- import { Events, ToolModes, SegmentationRepresentations, StrategyCallbacks, } from '../../enums';
7
+ import { Events, ToolModes, StrategyCallbacks } from '../../enums';
9
8
  import { drawCircle as drawCircleSvg } from '../../drawingSvg';
10
9
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
11
10
  import triggerAnnotationRenderForViewportUIDs from '../../utilities/triggerAnnotationRenderForViewportIds';
12
- import { getCurrentLabelmapImageIdForViewport, getSegmentation, getStackSegmentationImageIdsForViewport, } from '../../stateManagement/segmentation/segmentationState';
13
- import { getLockedSegmentIndices } from '../../stateManagement/segmentation/segmentLocking';
14
- import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
15
- import { getSegmentIndexColor } from '../../stateManagement/segmentation/config/segmentationColor';
16
- import { getActiveSegmentation } from '../../stateManagement/segmentation/getActiveSegmentation';
17
- class BrushTool extends BaseTool {
11
+ import LabelmapBaseTool from './LabelmapBaseTool';
12
+ class BrushTool extends LabelmapBaseTool {
18
13
  constructor(toolProps = {}, defaultToolProps = {
19
14
  supportedInteractionTypes: ['Mouse', 'Touch'],
20
15
  configuration: {
@@ -57,14 +52,6 @@ class BrushTool extends BaseTool {
57
52
  },
58
53
  }) {
59
54
  super(toolProps, defaultToolProps);
60
- this._previewData = {
61
- preview: null,
62
- element: null,
63
- timerStart: 0,
64
- timer: null,
65
- startPoint: [NaN, NaN],
66
- isDrag: false,
67
- };
68
55
  this.onSetToolPassive = (evt) => {
69
56
  this.disableCursor();
70
57
  };
@@ -183,144 +170,6 @@ class BrushTool extends BaseTool {
183
170
  this._hoverData = undefined;
184
171
  this.rejectPreview();
185
172
  }
186
- createEditData(element) {
187
- const enabledElement = getEnabledElement(element);
188
- const { viewport } = enabledElement;
189
- const activeSegmentation = getActiveSegmentation(viewport.id);
190
- if (!activeSegmentation) {
191
- const event = new CustomEvent(Enums.Events.ERROR_EVENT, {
192
- detail: {
193
- type: 'Segmentation',
194
- message: 'No active segmentation detected, create a segmentation representation before using the brush tool',
195
- },
196
- cancelable: true,
197
- });
198
- eventTarget.dispatchEvent(event);
199
- return null;
200
- }
201
- const { segmentationId } = activeSegmentation;
202
- const segmentsLocked = getLockedSegmentIndices(segmentationId);
203
- const { representationData } = getSegmentation(segmentationId);
204
- if (viewport instanceof BaseVolumeViewport) {
205
- const { volumeId } = representationData[SegmentationRepresentations.Labelmap];
206
- const actors = viewport.getActors();
207
- const isStackViewport = viewport instanceof StackViewport;
208
- if (isStackViewport) {
209
- const event = new CustomEvent(Enums.Events.ERROR_EVENT, {
210
- detail: {
211
- type: 'Segmentation',
212
- message: 'Cannot perform brush operation on the selected viewport',
213
- },
214
- cancelable: true,
215
- });
216
- eventTarget.dispatchEvent(event);
217
- return null;
218
- }
219
- const volumes = actors.map((actorEntry) => cache.getVolume(actorEntry.referencedId));
220
- const segmentationVolume = cache.getVolume(volumeId);
221
- const referencedVolumeIdToThreshold = volumes.find((volume) => csUtils.isEqual(volume.dimensions, segmentationVolume.dimensions))?.volumeId || volumes[0]?.volumeId;
222
- return {
223
- volumeId,
224
- referencedVolumeId: this.configuration.thresholdVolumeId ?? referencedVolumeIdToThreshold,
225
- segmentsLocked,
226
- };
227
- }
228
- else {
229
- const segmentationImageId = getCurrentLabelmapImageIdForViewport(viewport.id, segmentationId);
230
- if (!segmentationImageId) {
231
- return;
232
- }
233
- if (this.configuration.activeStrategy.includes('SPHERE')) {
234
- const referencedImageIds = viewport.getImageIds();
235
- const isValidVolumeForSphere = csUtils.isValidVolume(referencedImageIds);
236
- if (!isValidVolumeForSphere) {
237
- throw new Error('Volume is not reconstructable for sphere manipulation');
238
- }
239
- const volumeId = `${segmentationId}_${viewport.id}`;
240
- const volume = cache.getVolume(volumeId);
241
- if (volume) {
242
- return {
243
- imageId: segmentationImageId,
244
- segmentsLocked,
245
- override: {
246
- voxelManager: volume.voxelManager,
247
- imageData: volume.imageData,
248
- },
249
- };
250
- }
251
- else {
252
- const labelmapImageIds = getStackSegmentationImageIdsForViewport(viewport.id, segmentationId);
253
- if (!labelmapImageIds || labelmapImageIds.length === 1) {
254
- return {
255
- imageId: segmentationImageId,
256
- segmentsLocked,
257
- };
258
- }
259
- const volume = volumeLoader.createAndCacheVolumeFromImagesSync(volumeId, labelmapImageIds);
260
- return {
261
- imageId: segmentationImageId,
262
- segmentsLocked,
263
- override: {
264
- voxelManager: volume.voxelManager,
265
- imageData: volume.imageData,
266
- },
267
- };
268
- }
269
- }
270
- else {
271
- return {
272
- imageId: segmentationImageId,
273
- segmentsLocked,
274
- };
275
- }
276
- }
277
- }
278
- createHoverData(element, centerCanvas) {
279
- const enabledElement = getEnabledElement(element);
280
- const { viewport } = enabledElement;
281
- const camera = viewport.getCamera();
282
- const { viewPlaneNormal, viewUp } = camera;
283
- const viewportIdsToRender = [viewport.id];
284
- const { segmentIndex, segmentationId, segmentColor } = this.getActiveSegmentationData(viewport) || {};
285
- const brushCursor = {
286
- metadata: {
287
- viewPlaneNormal: [...viewPlaneNormal],
288
- viewUp: [...viewUp],
289
- FrameOfReferenceUID: viewport.getFrameOfReferenceUID(),
290
- referencedImageId: '',
291
- toolName: this.getToolName(),
292
- segmentColor,
293
- },
294
- data: {},
295
- };
296
- return {
297
- brushCursor,
298
- centerCanvas,
299
- segmentIndex,
300
- viewport,
301
- segmentationId,
302
- segmentColor,
303
- viewportIdsToRender,
304
- };
305
- }
306
- getActiveSegmentationData(viewport) {
307
- const viewportId = viewport.id;
308
- const activeRepresentation = getActiveSegmentation(viewportId);
309
- if (!activeRepresentation) {
310
- return;
311
- }
312
- const { segmentationId } = activeRepresentation;
313
- const segmentIndex = getActiveSegmentIndex(segmentationId);
314
- if (!segmentIndex) {
315
- return;
316
- }
317
- const segmentColor = getSegmentIndexColor(viewportId, segmentationId, segmentIndex);
318
- return {
319
- segmentIndex,
320
- segmentationId,
321
- segmentColor,
322
- };
323
- }
324
173
  updateCursor(evt) {
325
174
  const eventData = evt.detail;
326
175
  const { element } = eventData;
@@ -333,27 +182,6 @@ class BrushTool extends BaseTool {
333
182
  }
334
183
  triggerAnnotationRenderForViewportUIDs(this._hoverData.viewportIdsToRender);
335
184
  }
336
- getOperationData(element) {
337
- const editData = this._editData || this.createEditData(element);
338
- const { segmentIndex, segmentationId, brushCursor } = this._hoverData || this.createHoverData(element);
339
- const { data, metadata = {} } = brushCursor || {};
340
- const { viewPlaneNormal, viewUp } = metadata;
341
- const operationData = {
342
- ...editData,
343
- points: data?.handles?.points,
344
- segmentIndex,
345
- previewColors: this.configuration.preview.enabled
346
- ? this.configuration.preview.previewColors
347
- : null,
348
- viewPlaneNormal,
349
- toolGroupId: this.toolGroupId,
350
- segmentationId,
351
- viewUp,
352
- strategySpecificConfiguration: this.configuration.strategySpecificConfiguration,
353
- preview: this._previewData?.preview,
354
- };
355
- return operationData;
356
- }
357
185
  _calculateCursor(element, centerCanvas) {
358
186
  const enabledElement = getEnabledElement(element);
359
187
  const { viewport } = enabledElement;
@@ -410,24 +238,6 @@ class BrushTool extends BaseTool {
410
238
  const stats = this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.GetStatistics, segmentIndices);
411
239
  return stats;
412
240
  }
413
- rejectPreview(element = this._previewData.element) {
414
- if (!element || !this._previewData.preview) {
415
- return;
416
- }
417
- const enabledElement = getEnabledElement(element);
418
- this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.RejectPreview);
419
- this._previewData.preview = null;
420
- this._previewData.isDrag = false;
421
- }
422
- acceptPreview(element = this._previewData.element) {
423
- if (!element) {
424
- return;
425
- }
426
- const enabledElement = getEnabledElement(element);
427
- this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.AcceptPreview);
428
- this._previewData.isDrag = false;
429
- this._previewData.preview = null;
430
- }
431
241
  invalidateBrushCursor() {
432
242
  if (this._hoverData === undefined) {
433
243
  return;
@@ -0,0 +1,155 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ import { BaseTool } from '../base';
3
+ import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
4
+ export type PreviewData = {
5
+ preview: unknown;
6
+ timer?: number;
7
+ timerStart: number;
8
+ startPoint: Types.Point2;
9
+ element: HTMLDivElement;
10
+ isDrag: boolean;
11
+ };
12
+ export default class LabelmapBaseTool extends BaseTool {
13
+ protected _editData: {
14
+ override: {
15
+ voxelManager: Types.IVoxelManager<number>;
16
+ imageData: vtkImageData;
17
+ };
18
+ segmentsLocked: number[];
19
+ imageId?: string;
20
+ imageIds?: string[];
21
+ volumeId?: string;
22
+ referencedVolumeId?: string;
23
+ } | null;
24
+ protected _hoverData?: {
25
+ brushCursor: any;
26
+ segmentationId: string;
27
+ segmentIndex: number;
28
+ segmentColor: [number, number, number, number];
29
+ viewportIdsToRender: string[];
30
+ centerCanvas?: Array<number>;
31
+ viewport: Types.IViewport;
32
+ };
33
+ protected _previewData?: PreviewData;
34
+ constructor(toolProps: any, defaultToolProps: any);
35
+ createMemo(segmentId: string, segmentationVoxelManager: any, preview: any): void;
36
+ createEditData(element: any): {
37
+ volumeId: string;
38
+ referencedVolumeId: any;
39
+ segmentsLocked: number[] | [];
40
+ imageId?: undefined;
41
+ override?: undefined;
42
+ } | {
43
+ imageId: string;
44
+ segmentsLocked: number[] | [];
45
+ override: {
46
+ voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
47
+ imageData: vtkImageData;
48
+ };
49
+ volumeId?: undefined;
50
+ referencedVolumeId?: undefined;
51
+ } | {
52
+ imageId: string;
53
+ segmentsLocked: number[] | [];
54
+ volumeId?: undefined;
55
+ referencedVolumeId?: undefined;
56
+ override?: undefined;
57
+ };
58
+ protected createHoverData(element: any, centerCanvas?: any): {
59
+ brushCursor: {
60
+ metadata: {
61
+ viewPlaneNormal: Types.Point3;
62
+ viewUp: Types.Point3;
63
+ FrameOfReferenceUID: string;
64
+ referencedImageId: string;
65
+ toolName: string;
66
+ segmentColor: Types.Color;
67
+ };
68
+ data: {};
69
+ };
70
+ centerCanvas: any;
71
+ segmentIndex: number;
72
+ viewport: Types.IStackViewport | import("@cornerstonejs/core").VolumeViewport;
73
+ segmentationId: string;
74
+ segmentColor: Types.Color;
75
+ viewportIdsToRender: string[];
76
+ };
77
+ protected getActiveSegmentationData(viewport: any): {
78
+ segmentIndex: number;
79
+ segmentationId: string;
80
+ segmentColor: Types.Color;
81
+ };
82
+ protected getOperationData(element?: any): {
83
+ points: any;
84
+ segmentIndex: number;
85
+ previewColors: any;
86
+ viewPlaneNormal: any;
87
+ toolGroupId: string;
88
+ segmentationId: string;
89
+ viewUp: any;
90
+ strategySpecificConfiguration: any;
91
+ preview: unknown;
92
+ override: {
93
+ voxelManager: Types.IVoxelManager<number>;
94
+ imageData: vtkImageData;
95
+ };
96
+ segmentsLocked: number[];
97
+ imageId?: string;
98
+ imageIds?: string[];
99
+ volumeId?: string;
100
+ referencedVolumeId?: string;
101
+ } | {
102
+ points: any;
103
+ segmentIndex: number;
104
+ previewColors: any;
105
+ viewPlaneNormal: any;
106
+ toolGroupId: string;
107
+ segmentationId: string;
108
+ viewUp: any;
109
+ strategySpecificConfiguration: any;
110
+ preview: unknown;
111
+ volumeId: string;
112
+ referencedVolumeId: any;
113
+ segmentsLocked: number[] | [];
114
+ imageId?: undefined;
115
+ override?: undefined;
116
+ } | {
117
+ points: any;
118
+ segmentIndex: number;
119
+ previewColors: any;
120
+ viewPlaneNormal: any;
121
+ toolGroupId: string;
122
+ segmentationId: string;
123
+ viewUp: any;
124
+ strategySpecificConfiguration: any;
125
+ preview: unknown;
126
+ imageId: string;
127
+ segmentsLocked: number[] | [];
128
+ override: {
129
+ voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
130
+ imageData: vtkImageData;
131
+ };
132
+ volumeId?: undefined;
133
+ referencedVolumeId?: undefined;
134
+ } | {
135
+ points: any;
136
+ segmentIndex: number;
137
+ previewColors: any;
138
+ viewPlaneNormal: any;
139
+ toolGroupId: string;
140
+ segmentationId: string;
141
+ viewUp: any;
142
+ strategySpecificConfiguration: any;
143
+ preview: unknown;
144
+ imageId: string;
145
+ segmentsLocked: number[] | [];
146
+ volumeId?: undefined;
147
+ referencedVolumeId?: undefined;
148
+ override?: undefined;
149
+ };
150
+ addPreview(element?: HTMLDivElement, options?: {
151
+ acceptReject: boolean;
152
+ }): unknown;
153
+ rejectPreview(element?: HTMLDivElement): void;
154
+ acceptPreview(element?: HTMLDivElement): void;
155
+ }
@@ -0,0 +1,218 @@
1
+ import { getEnabledElement, cache, utilities as csUtils, Enums, eventTarget, BaseVolumeViewport, volumeLoader, } from '@cornerstonejs/core';
2
+ import { BaseTool } from '../base';
3
+ import SegmentationRepresentations from '../../enums/SegmentationRepresentations';
4
+ import { getActiveSegmentation } from '../../stateManagement/segmentation/getActiveSegmentation';
5
+ import { getLockedSegmentIndices } from '../../stateManagement/segmentation/segmentLocking';
6
+ import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
7
+ import { getClosestImageIdForStackViewport } from '../../utilities/annotationHydration';
8
+ import { getCurrentLabelmapImageIdForViewport } from '../../stateManagement/segmentation/getCurrentLabelmapImageIdForViewport';
9
+ import { getStackSegmentationImageIdsForViewport } from '../../stateManagement/segmentation/getStackSegmentationImageIdsForViewport';
10
+ import { getSegmentIndexColor } from '../../stateManagement/segmentation/config/segmentationColor';
11
+ import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
12
+ import { StrategyCallbacks } from '../../enums';
13
+ export default class LabelmapBaseTool extends BaseTool {
14
+ constructor(toolProps, defaultToolProps) {
15
+ super(toolProps, defaultToolProps);
16
+ this._previewData = {
17
+ preview: null,
18
+ element: null,
19
+ timerStart: 0,
20
+ timer: null,
21
+ startPoint: [NaN, NaN],
22
+ isDrag: false,
23
+ };
24
+ }
25
+ createMemo(segmentId, segmentationVoxelManager, preview) {
26
+ console.warn('LabelmapBaseTool.createMemo not implemented yet');
27
+ }
28
+ createEditData(element) {
29
+ const enabledElement = getEnabledElement(element);
30
+ const { viewport } = enabledElement;
31
+ const activeSegmentation = getActiveSegmentation(viewport.id);
32
+ if (!activeSegmentation) {
33
+ const event = new CustomEvent(Enums.Events.ERROR_EVENT, {
34
+ detail: {
35
+ type: 'Segmentation',
36
+ message: 'No active segmentation detected, create a segmentation representation before using the brush tool',
37
+ },
38
+ cancelable: true,
39
+ });
40
+ eventTarget.dispatchEvent(event);
41
+ return null;
42
+ }
43
+ const { segmentationId } = activeSegmentation;
44
+ const segmentsLocked = getLockedSegmentIndices(segmentationId);
45
+ const { representationData } = getSegmentation(segmentationId);
46
+ if (viewport instanceof BaseVolumeViewport) {
47
+ const { volumeId } = representationData[SegmentationRepresentations.Labelmap];
48
+ const actors = viewport.getActors();
49
+ const isStackViewport = viewport instanceof getClosestImageIdForStackViewport;
50
+ if (isStackViewport) {
51
+ const event = new CustomEvent(Enums.Events.ERROR_EVENT, {
52
+ detail: {
53
+ type: 'Segmentation',
54
+ message: 'Cannot perform brush operation on the selected viewport',
55
+ },
56
+ cancelable: true,
57
+ });
58
+ eventTarget.dispatchEvent(event);
59
+ return null;
60
+ }
61
+ const volumes = actors.map((actorEntry) => cache.getVolume(actorEntry.referencedId));
62
+ const segmentationVolume = cache.getVolume(volumeId);
63
+ const referencedVolumeIdToThreshold = volumes.find((volume) => csUtils.isEqual(volume.dimensions, segmentationVolume.dimensions))?.volumeId || volumes[0]?.volumeId;
64
+ return {
65
+ volumeId,
66
+ referencedVolumeId: this.configuration.thresholdVolumeId ?? referencedVolumeIdToThreshold,
67
+ segmentsLocked,
68
+ };
69
+ }
70
+ else {
71
+ const segmentationImageId = getCurrentLabelmapImageIdForViewport(viewport.id, segmentationId);
72
+ if (!segmentationImageId) {
73
+ return;
74
+ }
75
+ if (this.configuration.activeStrategy.includes('SPHERE')) {
76
+ const referencedImageIds = viewport.getImageIds();
77
+ const isValidVolumeForSphere = csUtils.isValidVolume(referencedImageIds);
78
+ if (!isValidVolumeForSphere) {
79
+ throw new Error('Volume is not reconstructable for sphere manipulation');
80
+ }
81
+ const volumeId = `${segmentationId}_${viewport.id}`;
82
+ const volume = cache.getVolume(volumeId);
83
+ if (volume) {
84
+ return {
85
+ imageId: segmentationImageId,
86
+ segmentsLocked,
87
+ override: {
88
+ voxelManager: volume.voxelManager,
89
+ imageData: volume.imageData,
90
+ },
91
+ };
92
+ }
93
+ else {
94
+ const labelmapImageIds = getStackSegmentationImageIdsForViewport(viewport.id, segmentationId);
95
+ if (!labelmapImageIds || labelmapImageIds.length === 1) {
96
+ return {
97
+ imageId: segmentationImageId,
98
+ segmentsLocked,
99
+ };
100
+ }
101
+ const volume = volumeLoader.createAndCacheVolumeFromImagesSync(volumeId, labelmapImageIds);
102
+ return {
103
+ imageId: segmentationImageId,
104
+ segmentsLocked,
105
+ override: {
106
+ voxelManager: volume.voxelManager,
107
+ imageData: volume.imageData,
108
+ },
109
+ };
110
+ }
111
+ }
112
+ else {
113
+ return {
114
+ imageId: segmentationImageId,
115
+ segmentsLocked,
116
+ };
117
+ }
118
+ }
119
+ }
120
+ createHoverData(element, centerCanvas) {
121
+ const enabledElement = getEnabledElement(element);
122
+ const { viewport } = enabledElement;
123
+ const camera = viewport.getCamera();
124
+ const { viewPlaneNormal, viewUp } = camera;
125
+ const viewportIdsToRender = [viewport.id];
126
+ const { segmentIndex, segmentationId, segmentColor } = this.getActiveSegmentationData(viewport) || {};
127
+ const brushCursor = {
128
+ metadata: {
129
+ viewPlaneNormal: [...viewPlaneNormal],
130
+ viewUp: [...viewUp],
131
+ FrameOfReferenceUID: viewport.getFrameOfReferenceUID(),
132
+ referencedImageId: '',
133
+ toolName: this.getToolName(),
134
+ segmentColor,
135
+ },
136
+ data: {},
137
+ };
138
+ return {
139
+ brushCursor,
140
+ centerCanvas,
141
+ segmentIndex,
142
+ viewport,
143
+ segmentationId,
144
+ segmentColor,
145
+ viewportIdsToRender,
146
+ };
147
+ }
148
+ getActiveSegmentationData(viewport) {
149
+ const viewportId = viewport.id;
150
+ const activeRepresentation = getActiveSegmentation(viewportId);
151
+ if (!activeRepresentation) {
152
+ return;
153
+ }
154
+ const { segmentationId } = activeRepresentation;
155
+ const segmentIndex = getActiveSegmentIndex(segmentationId);
156
+ if (!segmentIndex) {
157
+ return;
158
+ }
159
+ const segmentColor = getSegmentIndexColor(viewportId, segmentationId, segmentIndex);
160
+ return {
161
+ segmentIndex,
162
+ segmentationId,
163
+ segmentColor,
164
+ };
165
+ }
166
+ getOperationData(element) {
167
+ const editData = this._editData || this.createEditData(element);
168
+ const { segmentIndex, segmentationId, brushCursor } = this._hoverData || this.createHoverData(element);
169
+ const { data, metadata = {} } = brushCursor || {};
170
+ const { viewPlaneNormal, viewUp } = metadata;
171
+ const operationData = {
172
+ ...editData,
173
+ points: data?.handles?.points,
174
+ segmentIndex,
175
+ previewColors: this.configuration.preview?.enabled || this._previewData.preview
176
+ ? this.configuration.preview.previewColors
177
+ : null,
178
+ viewPlaneNormal,
179
+ toolGroupId: this.toolGroupId,
180
+ segmentationId,
181
+ viewUp,
182
+ strategySpecificConfiguration: this.configuration.strategySpecificConfiguration,
183
+ preview: this._previewData?.preview,
184
+ };
185
+ return operationData;
186
+ }
187
+ addPreview(element = this._previewData.element, options) {
188
+ const acceptReject = options?.acceptReject;
189
+ if (acceptReject === true) {
190
+ this.acceptPreview(element);
191
+ }
192
+ else if (acceptReject === false) {
193
+ this.rejectPreview(element);
194
+ }
195
+ const enabledElement = getEnabledElement(element);
196
+ this._previewData.preview = this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.AddPreview);
197
+ this._previewData.isDrag = true;
198
+ return this._previewData.preview;
199
+ }
200
+ rejectPreview(element = this._previewData.element) {
201
+ if (!element || !this._previewData.preview) {
202
+ return;
203
+ }
204
+ const enabledElement = getEnabledElement(element);
205
+ this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.RejectPreview);
206
+ this._previewData.preview = null;
207
+ this._previewData.isDrag = false;
208
+ }
209
+ acceptPreview(element = this._previewData.element) {
210
+ if (!element) {
211
+ return;
212
+ }
213
+ const enabledElement = getEnabledElement(element);
214
+ this.applyActiveStrategyCallback(enabledElement, this.getOperationData(element), StrategyCallbacks.AcceptPreview);
215
+ this._previewData.isDrag = false;
216
+ this._previewData.preview = null;
217
+ }
218
+ }
@@ -76,6 +76,7 @@ export default class BrushStrategy {
76
76
  setValue: (brushStrategy: any, func: any) => void;
77
77
  preview: (brushStrategy: any, func: any) => void;
78
78
  computeInnerCircleRadius: (brushStrategy: any, func: any) => void;
79
+ addPreview: (brushStrategy: any, func: any) => void;
79
80
  getStatistics: (brushStrategy: any, func: any) => void;
80
81
  compositions: any;
81
82
  };
@@ -92,6 +93,7 @@ export default class BrushStrategy {
92
93
  onInteractionStart: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => void;
93
94
  onInteractionEnd: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => void;
94
95
  rejectPreview: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => void;
96
+ addPreview: (enabledElement: any, operationData: LabelmapToolOperationDataAny) => any;
95
97
  acceptPreview: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => void;
96
98
  preview: (enabledElement: Types.IEnabledElement, operationData: LabelmapToolOperationDataAny) => unknown;
97
99
  setValue: (operationData: InitializedOperationData, data: any) => void;
@@ -17,6 +17,7 @@ export default class BrushStrategy {
17
17
  [StrategyCallbacks.INTERNAL_setValue]: addSingletonMethod(StrategyCallbacks.INTERNAL_setValue),
18
18
  [StrategyCallbacks.Preview]: addSingletonMethod(StrategyCallbacks.Preview, false),
19
19
  [StrategyCallbacks.ComputeInnerCircleRadius]: addListMethod(StrategyCallbacks.ComputeInnerCircleRadius),
20
+ [StrategyCallbacks.AddPreview]: addListMethod(StrategyCallbacks.AddPreview),
20
21
  [StrategyCallbacks.GetStatistics]: addSingletonMethod(StrategyCallbacks.GetStatistics),
21
22
  compositions: null,
22
23
  }; }
@@ -58,6 +59,13 @@ export default class BrushStrategy {
58
59
  }
59
60
  this._onInteractionStart.forEach((func) => func.call(this, initializedData));
60
61
  };
62
+ this.addPreview = (enabledElement, operationData) => {
63
+ const initializedData = this.initialize(enabledElement, operationData, StrategyCallbacks.AddPreview);
64
+ if (!initializedData) {
65
+ return;
66
+ }
67
+ return initializedData.preview || initializedData;
68
+ };
61
69
  this.configurationName = name;
62
70
  this.compositions = initializers;
63
71
  initializers.forEach((initializer) => {
@@ -11,13 +11,25 @@ export default {
11
11
  }
12
12
  },
13
13
  [StrategyCallbacks.OnInteractionStart]: (operationData) => {
14
- const { segmentIndex, previewSegmentIndex, segmentationVoxelManager, centerIJK, strategySpecificConfiguration, imageVoxelManager, segmentationImageData, preview, } = operationData;
14
+ const { segmentIndex, previewSegmentIndex, segmentationVoxelManager, centerIJK, strategySpecificConfiguration, viewPlaneNormal, segmentationImageData, preview, } = operationData;
15
15
  if (!strategySpecificConfiguration?.useCenterSegmentIndex) {
16
16
  return;
17
17
  }
18
18
  delete strategySpecificConfiguration.centerSegmentIndex;
19
19
  let hasSegmentIndex = false;
20
20
  let hasPreviewIndex = false;
21
+ const nestedBounds = [
22
+ ...segmentationVoxelManager.getBoundsIJK(),
23
+ ];
24
+ if (Math.abs(viewPlaneNormal[0]) > 0.8) {
25
+ nestedBounds[0] = [centerIJK[0], centerIJK[0]];
26
+ }
27
+ else if (Math.abs(viewPlaneNormal[1]) > 0.8) {
28
+ nestedBounds[1] = [centerIJK[1], centerIJK[1]];
29
+ }
30
+ else if (Math.abs(viewPlaneNormal[2]) > 0.8) {
31
+ nestedBounds[2] = [centerIJK[2], centerIJK[2]];
32
+ }
21
33
  const callback = ({ value }) => {
22
34
  hasSegmentIndex ||= value === segmentIndex;
23
35
  hasPreviewIndex ||= value === previewSegmentIndex;
@@ -25,6 +37,7 @@ export default {
25
37
  segmentationVoxelManager.forEach(callback, {
26
38
  imageData: segmentationImageData,
27
39
  isInObject: operationData.isInObject,
40
+ boundsIJK: nestedBounds,
28
41
  });
29
42
  if (!hasSegmentIndex && !hasPreviewIndex) {
30
43
  return;
@@ -2,6 +2,7 @@ import type { Types } from '@cornerstonejs/core';
2
2
  type Annotation = {
3
3
  annotationUID?: string;
4
4
  parentAnnotationUID?: string;
5
+ interpolationUID?: string;
5
6
  childAnnotationUIDs?: string[];
6
7
  highlighted?: boolean;
7
8
  isLocked?: boolean;
@@ -31,7 +32,7 @@ type Annotation = {
31
32
  [key: string]: unknown;
32
33
  };
33
34
  [key: string]: unknown;
34
- cachedStats?: unknown;
35
+ cachedStats?: Record<string, unknown>;
35
36
  };
36
37
  };
37
38
  type Annotations = Array<Annotation>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "2.5.3",
3
+ "version": "2.6.0",
4
4
  "description": "Cornerstone3D Tools",
5
5
  "types": "./dist/esm/index.d.ts",
6
6
  "module": "./dist/esm/index.js",
@@ -104,7 +104,7 @@
104
104
  "canvas": "^2.11.2"
105
105
  },
106
106
  "peerDependencies": {
107
- "@cornerstonejs/core": "^2.5.3",
107
+ "@cornerstonejs/core": "^2.6.0",
108
108
  "@kitware/vtk.js": "32.1.1",
109
109
  "@types/d3-array": "^3.0.4",
110
110
  "@types/d3-interpolate": "^3.0.1",
@@ -123,5 +123,5 @@
123
123
  "type": "individual",
124
124
  "url": "https://ohif.org/donate"
125
125
  },
126
- "gitHead": "f2996db78ea77e524dabd88d176177064a0f2ef3"
126
+ "gitHead": "d5c8036b08e8927a3fbbc1af6a0557687fc0645c"
127
127
  }