@cornerstonejs/tools 5.0.16 → 5.1.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 (67) hide show
  1. package/dist/esm/stateManagement/segmentation/helpers/labelmapImageMapperSupport.js +1 -1
  2. package/dist/esm/stateManagement/segmentation/labelmapModel/normalizeLabelmapSegmentationData.js +1 -1
  3. package/dist/esm/stateManagement/segmentation/labelmapModel/privateLabelmap.js +2 -2
  4. package/dist/esm/stateManagement/segmentation/utilities/getViewportAssociatedToSegmentation.js +2 -1
  5. package/dist/esm/synchronizers/callbacks/cameraSyncCallback.js +19 -2
  6. package/dist/esm/synchronizers/callbacks/imageSliceSyncCallback.js +3 -0
  7. package/dist/esm/synchronizers/callbacks/voiSyncCallback.js +13 -1
  8. package/dist/esm/tools/AdvancedMagnifyTool.js +36 -10
  9. package/dist/esm/tools/CrosshairsTool.js +100 -49
  10. package/dist/esm/tools/MagnifyTool.d.ts +1 -1
  11. package/dist/esm/tools/MagnifyTool.js +27 -6
  12. package/dist/esm/tools/OrientationControllerTool.js +3 -2
  13. package/dist/esm/tools/OrientationMarkerTool.js +3 -0
  14. package/dist/esm/tools/OverlayGridTool.js +2 -1
  15. package/dist/esm/tools/ReferenceCursors.d.ts +1 -1
  16. package/dist/esm/tools/ReferenceCursors.js +24 -3
  17. package/dist/esm/tools/ReferenceLinesTool.js +5 -4
  18. package/dist/esm/tools/ScaleOverlayTool.js +2 -1
  19. package/dist/esm/tools/TrackballRotateTool.js +6 -4
  20. package/dist/esm/tools/VolumeCroppingControlTool.js +11 -6
  21. package/dist/esm/tools/VolumeCroppingTool.js +6 -4
  22. package/dist/esm/tools/VolumeRotateTool.js +4 -2
  23. package/dist/esm/tools/WindowLevelRegionTool.js +27 -3
  24. package/dist/esm/tools/WindowLevelTool.js +4 -8
  25. package/dist/esm/tools/annotation/DragProbeTool.js +2 -1
  26. package/dist/esm/tools/annotation/ETDRSGridTool.js +3 -2
  27. package/dist/esm/tools/annotation/EllipticalROITool.js +2 -1
  28. package/dist/esm/tools/annotation/LabelTool.js +2 -1
  29. package/dist/esm/tools/annotation/LivewireContourTool.js +12 -1
  30. package/dist/esm/tools/annotation/RectangleROITool.js +2 -1
  31. package/dist/esm/tools/annotation/UltrasoundPleuraBLineTool/UltrasoundPleuraBLineTool.js +2 -1
  32. package/dist/esm/tools/annotation/WholeBodySegmentTool.js +3 -2
  33. package/dist/esm/tools/annotation/planarFreehandROITool/drawLoop.js +1 -1
  34. package/dist/esm/tools/base/AnnotationTool.js +6 -0
  35. package/dist/esm/tools/base/GrowCutBaseTool.js +3 -2
  36. package/dist/esm/tools/displayTools/Contour/contourDisplay.js +2 -1
  37. package/dist/esm/tools/displayTools/Contour/contourHandler/handleContourSegmentation.js +3 -2
  38. package/dist/esm/tools/displayTools/Labelmap/labelmapRenderPlan/legacyVolumePlan.js +21 -9
  39. package/dist/esm/tools/displayTools/Labelmap/labelmapRenderPlan/planarGenericVolumeLabelmap.js +1 -1
  40. package/dist/esm/tools/displayTools/Labelmap/removeLabelmapRepresentationData.js +1 -1
  41. package/dist/esm/tools/displayTools/Labelmap/syncStackLabelmapActors.js +36 -0
  42. package/dist/esm/tools/segmentation/CircleScissorsTool.js +2 -1
  43. package/dist/esm/tools/segmentation/RectangleScissorsTool.js +2 -1
  44. package/dist/esm/tools/segmentation/SegmentBidirectionalTool.js +2 -1
  45. package/dist/esm/tools/segmentation/strategies/fillRectangle.js +2 -1
  46. package/dist/esm/types/LabelmapTypes.d.ts +1 -1
  47. package/dist/esm/utilities/cine/playClip.js +28 -0
  48. package/dist/esm/utilities/genericViewportToolHelpers.d.ts +11 -0
  49. package/dist/esm/utilities/genericViewportToolHelpers.js +37 -0
  50. package/dist/esm/utilities/getVOIMultipliers.js +1 -3
  51. package/dist/esm/utilities/getViewportICamera.js +3 -2
  52. package/dist/esm/utilities/index.d.ts +2 -1
  53. package/dist/esm/utilities/index.js +2 -1
  54. package/dist/esm/utilities/math/polyline/getSubPixelSpacingAndXYDirections.js +7 -3
  55. package/dist/esm/utilities/planar/filterAnnotationsForDisplay.js +7 -2
  56. package/dist/esm/utilities/segmentation/getSegmentIndexAtWorldPoint.js +2 -1
  57. package/dist/esm/utilities/segmentation/growCut/runGrowCutForSphere.js +3 -2
  58. package/dist/esm/utilities/setViewportCamera.d.ts +3 -0
  59. package/dist/esm/utilities/setViewportCamera.js +58 -0
  60. package/dist/esm/utilities/stackPrefetch/stackPrefetchUtils.js +6 -2
  61. package/dist/esm/utilities/viewport/isViewportPreScaled.js +2 -13
  62. package/dist/esm/utilities/voi/colorbar/ViewportColorbar.js +17 -5
  63. package/dist/esm/utilities/voi/windowlevel/extractWindowLevelRegionToolData.d.ts +1 -1
  64. package/dist/esm/utilities/voi/windowlevel/extractWindowLevelRegionToolData.js +28 -1
  65. package/dist/esm/version.d.ts +1 -1
  66. package/dist/esm/version.js +1 -1
  67. package/package.json +4 -4
@@ -4,6 +4,7 @@ import { drawPolyline as drawPolylineSvg } from '../../drawingSvg';
4
4
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
5
5
  import { Events } from '../../enums';
6
6
  import triggerAnnotationRenderForViewportUIDs from '../../utilities/triggerAnnotationRenderForViewportIds';
7
+ import getViewportICamera from '../../utilities/getViewportICamera';
7
8
  import { growCut } from '../../utilities/segmentation';
8
9
  import GrowCutBaseTool from '../base/GrowCutBaseTool';
9
10
  const NEGATIVE_PIXEL_RANGE = [-Infinity, -995];
@@ -167,7 +168,7 @@ class WholeBodySegmentTool extends GrowCutBaseTool {
167
168
  return [ijkLineP1, ijkLineP2];
168
169
  }
169
170
  _getCuboidIJKEdgePointsFromProjectedWorldPoint(viewport, worldEdgePoint) {
170
- const { viewPlaneNormal } = viewport.getCamera();
171
+ const { viewPlaneNormal } = getViewportICamera(viewport);
171
172
  return this._projectWorldPointAcrossSlices(viewport, worldEdgePoint, viewPlaneNormal);
172
173
  }
173
174
  _getWorldCuboidCornerPoints(viewport, worldSquarePoints) {
@@ -202,7 +203,7 @@ class WholeBodySegmentTool extends GrowCutBaseTool {
202
203
  const volume = this._getViewportVolume(viewport);
203
204
  const { dimensions } = volume;
204
205
  const ijkPoint = transformWorldToIndex(volume.imageData, worldPoint);
205
- const { viewUp, viewPlaneNormal } = viewport.getCamera();
206
+ const { viewUp, viewPlaneNormal } = getViewportICamera(viewport);
206
207
  const vecRow = vec3.cross(vec3.create(), viewUp, viewPlaneNormal);
207
208
  const axis = vecRow.findIndex((n) => csUtils.isEqual(Math.abs(n), 1));
208
209
  const ijkLineP1 = [...ijkPoint];
@@ -13,7 +13,6 @@ import { removeAnnotation } from '../../../stateManagement/annotation/annotation
13
13
  import { ContourWindingDirection } from '../../../types/ContourAnnotation';
14
14
  const { addCanvasPointsToArray, pointsAreWithinCloseContourProximity, getFirstLineSegmentIntersectionIndexes, getSubPixelSpacingAndXYDirections, } = polyline;
15
15
  function activateDraw(evt, annotation, viewportIdsToRender) {
16
- this.isDrawing = true;
17
16
  const eventDetail = evt.detail;
18
17
  const { currentPoints, element } = eventDetail;
19
18
  const canvasPos = currentPoints.canvas;
@@ -25,6 +24,7 @@ function activateDraw(evt, annotation, viewportIdsToRender) {
25
24
  if (!spacing || !xDir || !yDir) {
26
25
  return;
27
26
  }
27
+ this.isDrawing = true;
28
28
  this.drawData = {
29
29
  canvasPoints: [canvasPos],
30
30
  polylineIndex: 0,
@@ -314,6 +314,12 @@ class AnnotationTool extends AnnotationDisplayTool {
314
314
  else if (viewport instanceof BaseVolumeViewport) {
315
315
  referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
316
316
  }
317
+ else if (csUtils.isGenericViewport(viewport)) {
318
+ const genericViewport = viewport;
319
+ referencedImageId = genericViewport.getViewReference?.({
320
+ points: [points[0]],
321
+ })?.referencedImageId;
322
+ }
317
323
  else {
318
324
  throw new Error('Unsupported viewport type');
319
325
  }
@@ -1,5 +1,6 @@
1
1
  import { getEnabledElement, utilities as csUtils, cache, getRenderingEngine, volumeLoader, imageLoader, ImageVolume, } from '@cornerstonejs/core';
2
2
  import { BaseTool } from '../base';
3
+ import getViewportICamera from '../../utilities/getViewportICamera';
3
4
  import { SegmentationRepresentations } from '../../enums';
4
5
  import { segmentIndex as segmentIndexController, state as segmentationState, activeSegmentation, } from '../../stateManagement/segmentation';
5
6
  import { triggerSegmentationDataModified } from '../../stateManagement/segmentation/triggerSegmentationEvents';
@@ -28,7 +29,7 @@ class GrowCutBaseTool extends BaseTool {
28
29
  const { world: worldPoint } = currentPoints;
29
30
  const enabledElement = getEnabledElement(element);
30
31
  const { viewport, renderingEngine } = enabledElement;
31
- const { viewUp } = viewport.getCamera();
32
+ const { viewUp } = getViewportICamera(viewport);
32
33
  const { segmentationId, segmentIndex, labelmapVolumeId, referencedVolumeId, } = await this.getLabelmapSegmentationData(viewport);
33
34
  if (!this._isOrthogonalView(viewport, referencedVolumeId)) {
34
35
  throw new Error('Oblique view is not supported yet');
@@ -223,7 +224,7 @@ class GrowCutBaseTool extends BaseTool {
223
224
  _isOrthogonalView(viewport, referencedVolumeId) {
224
225
  const volume = cache.getVolume(referencedVolumeId);
225
226
  const volumeImageData = volume.imageData;
226
- const camera = viewport.getCamera();
227
+ const camera = getViewportICamera(viewport);
227
228
  const { ijkVecColDir, ijkVecSliceDir } = csUtils.getVolumeDirectionVectors(volumeImageData, camera);
228
229
  return [ijkVecColDir, ijkVecSliceDir].every((vec) => csUtils.isEqual(Math.abs(vec[0]), 1) ||
229
230
  csUtils.isEqual(Math.abs(vec[1]), 1) ||
@@ -1,6 +1,7 @@
1
1
  import { cache, getEnabledElementByViewportId, Enums, utilities, } from '@cornerstonejs/core';
2
2
  import Representations from '../../../enums/SegmentationRepresentations';
3
3
  import { handleContourSegmentation } from './contourHandler/handleContourSegmentation';
4
+ import getViewportICamera from '../../../utilities/getViewportICamera';
4
5
  import { getSegmentation } from '../../../stateManagement/segmentation/getSegmentation';
5
6
  import removeContourFromElement from './removeContourFromElement';
6
7
  import { getPolySeg } from '../../../config';
@@ -51,7 +52,7 @@ async function render(viewport, contourRepresentation) {
51
52
  return;
52
53
  }
53
54
  let hasContourDataButNotMatchingViewport = false;
54
- const viewportNormal = viewport.getCamera().viewPlaneNormal;
55
+ const viewportNormal = getViewportICamera(viewport).viewPlaneNormal;
55
56
  if (contourData.annotationUIDsMap) {
56
57
  hasContourDataButNotMatchingViewport = !_checkContourNormalsMatchViewport(contourData.annotationUIDsMap, viewportNormal);
57
58
  }
@@ -2,6 +2,7 @@ import { addAnnotation } from '../../../../stateManagement/annotation/annotation
2
2
  import { cache, utilities } from '@cornerstonejs/core';
3
3
  import { getClosestImageIdForStackViewport } from '../../../../utilities/annotationHydration';
4
4
  import { addContourSegmentationAnnotation } from '../../../../utilities/contourSegmentation';
5
+ import getViewportICamera from '../../../../utilities/getViewportICamera';
5
6
  import { validateGeometry } from './utils';
6
7
  import { SegmentationRepresentations } from '../../../../enums';
7
8
  import { segmentationStyle } from '../../../../stateManagement/segmentation/SegmentationStyle';
@@ -31,7 +32,7 @@ function addContourSetsToElement(viewport, geometryIds, contourRepresentation) {
31
32
  segmentIndex,
32
33
  });
33
34
  const contourSet = geometry.data;
34
- const viewPlaneNormal = viewport.getCamera().viewPlaneNormal;
35
+ const viewPlaneNormal = getViewportICamera(viewport).viewPlaneNormal;
35
36
  contourSet.contours.forEach((contour) => {
36
37
  const { points, color, id } = contour;
37
38
  const referencedImageId = getClosestImageIdForStackViewport(viewport, points[0], viewPlaneNormal);
@@ -60,7 +61,7 @@ function addContourSetsToElement(viewport, geometryIds, contourRepresentation) {
60
61
  referencedImageId,
61
62
  toolName: 'PlanarFreehandContourSegmentationTool',
62
63
  FrameOfReferenceUID: viewport.getFrameOfReferenceUID(),
63
- viewPlaneNormal: viewport.getCamera().viewPlaneNormal,
64
+ viewPlaneNormal,
64
65
  },
65
66
  };
66
67
  const annotationGroupSelector = viewport.element;
@@ -47,7 +47,7 @@ async function mountLegacyVolumeLabelmap({ config, labelMapData, segmentation, s
47
47
  }
48
48
  labelmapLayers.push({
49
49
  labelmapId: volumeId,
50
- type: 'volume',
50
+ storageKind: 'volume',
51
51
  volumeId,
52
52
  imageIds: cache.getVolume(volumeId)?.imageIds,
53
53
  });
@@ -56,16 +56,28 @@ async function mountLegacyVolumeLabelmap({ config, labelMapData, segmentation, s
56
56
  let useIndependentComponents = blendMode === Enums.BlendModes.LABELMAP_EDGE_PROJECTION_BLEND;
57
57
  if (useIndependentComponents) {
58
58
  const referenceVolumeId = volumeCompatibleViewport.getVolumeId?.();
59
- const baseVolume = cache.getVolume(referenceVolumeId);
60
- const segVolume = cache.getVolume(labelmapLayers[0]?.volumeId);
61
- const segDims = segVolume.dimensions;
62
- const refDims = baseVolume.dimensions;
63
- if (segDims[0] !== refDims[0] ||
64
- segDims[1] !== refDims[1] ||
65
- segDims[2] !== refDims[2]) {
59
+ const segLabelmapVolumeId = labelmapLayers[0]?.volumeId;
60
+ const baseVolume = referenceVolumeId
61
+ ? cache.getVolume(referenceVolumeId)
62
+ : undefined;
63
+ const segVolume = segLabelmapVolumeId
64
+ ? cache.getVolume(segLabelmapVolumeId)
65
+ : undefined;
66
+ if (!baseVolume || !segVolume) {
66
67
  useIndependentComponents = false;
67
68
  blendMode = Enums.BlendModes.MAXIMUM_INTENSITY_BLEND;
68
- console.debug('Dimensions mismatch - falling back to regular volume addition');
69
+ console.debug('Independent components unavailable (missing reference or segmentation volume) - falling back to regular volume addition');
70
+ }
71
+ else {
72
+ const segDims = segVolume.dimensions;
73
+ const refDims = baseVolume.dimensions;
74
+ if (segDims[0] !== refDims[0] ||
75
+ segDims[1] !== refDims[1] ||
76
+ segDims[2] !== refDims[2]) {
77
+ useIndependentComponents = false;
78
+ blendMode = Enums.BlendModes.MAXIMUM_INTENSITY_BLEND;
79
+ console.debug('Dimensions mismatch - falling back to regular volume addition');
80
+ }
69
81
  }
70
82
  }
71
83
  const volumeInputs = labelmapLayers.map((layer) => ({
@@ -36,7 +36,7 @@ async function addLabelmapToPlanarGenericViewport(args) {
36
36
  referencedId: layer.labelmapId,
37
37
  });
38
38
  const dataId = representationUID;
39
- utilities.genericViewportDataSetMetadataProvider.add(dataId, {
39
+ utilities.genericViewportDisplaySetMetadataProvider.add(dataId, {
40
40
  kind: 'planar',
41
41
  imageIds: volume.imageIds,
42
42
  initialImageIdIndex: Math.min(currentImageIdIndex, Math.max(volume.imageIds.length - 1, 0)),
@@ -9,7 +9,7 @@ function removeLabelmapRepresentationData(viewport, segmentationId, actorEntry)
9
9
  if (typeof dataViewport.removeData !== 'function') {
10
10
  return false;
11
11
  }
12
- utilities.genericViewportDataSetMetadataProvider.remove(representationUID);
12
+ utilities.genericViewportDisplaySetMetadataProvider.remove(representationUID);
13
13
  dataViewport.removeData(representationUID);
14
14
  return true;
15
15
  }
@@ -1,5 +1,6 @@
1
1
  import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
2
2
  import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
3
+ import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
3
4
  import { ActorRenderMode, cache, utilities, } from '@cornerstonejs/core';
4
5
  import { triggerSegmentationRender } from '../../../stateManagement/segmentation/SegmentationRenderingEngine';
5
6
  import { updateLabelmapSegmentationImageReferences } from '../../../stateManagement/segmentation/updateLabelmapSegmentationImageReferences';
@@ -22,6 +23,7 @@ export function syncStackLabelmapActors(viewport, segmentationId) {
22
23
  const derivedImageIdSet = new Set(derivedImageIds);
23
24
  const labelmapActorEntries = getLabelmapActorEntries(viewport.id, segmentationId) ?? [];
24
25
  const staleActorEntries = labelmapActorEntries.filter((actorEntry) => !derivedImageIdSet.has(actorEntry.referencedId));
26
+ const inheritedLabelmapStyle = captureLabelmapActorStyle(staleActorEntries[0]?.actor);
25
27
  let shouldTriggerSegmentationRender = false;
26
28
  let shouldRenderViewport = staleActorEntries.length > 0;
27
29
  if (staleActorEntries.length) {
@@ -104,6 +106,7 @@ export function syncStackLabelmapActors(viewport, segmentationId) {
104
106
  representationUID,
105
107
  callback: ({ imageActor }) => {
106
108
  imageActor.getMapper().setInputData(imageData);
109
+ applyInheritedLabelmapStyle(imageActor, inheritedLabelmapStyle);
107
110
  },
108
111
  },
109
112
  ]);
@@ -133,3 +136,36 @@ export function syncStackLabelmapActors(viewport, segmentationId) {
133
136
  viewport.render();
134
137
  }
135
138
  }
139
+ function captureLabelmapActorStyle(actor) {
140
+ const prop = actor?.getProperty?.();
141
+ if (!prop) {
142
+ return undefined;
143
+ }
144
+ return {
145
+ cfun: prop.getRGBTransferFunction?.(0),
146
+ ofun: prop.getScalarOpacity?.(0),
147
+ };
148
+ }
149
+ function applyInheritedLabelmapStyle(actor, style) {
150
+ const prop = actor?.getProperty?.();
151
+ if (!prop) {
152
+ return;
153
+ }
154
+ if (style?.cfun && prop.setRGBTransferFunction) {
155
+ prop.setRGBTransferFunction(0, style.cfun);
156
+ }
157
+ if (style?.ofun && prop.setScalarOpacity) {
158
+ prop.setScalarOpacity(0, style.ofun);
159
+ }
160
+ else if (prop.setScalarOpacity) {
161
+ const ofun = vtkPiecewiseFunction.newInstance();
162
+ ofun.addPoint(0, 0);
163
+ ofun.addPoint(1, 1);
164
+ ofun.setClamping(false);
165
+ prop.setScalarOpacity(0, ofun);
166
+ }
167
+ prop.setUseLookupTableScalarRange?.(true);
168
+ prop.setInterpolationTypeToNearest?.();
169
+ actor?.setForceTranslucent?.(true);
170
+ actor?.setForceOpaque?.(false);
171
+ }
@@ -6,6 +6,7 @@ import { Events } from '../../enums';
6
6
  import { drawCircle as drawCircleSvg } from '../../drawingSvg';
7
7
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
8
8
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
9
+ import getViewportICamera from '../../utilities/getViewportICamera';
9
10
  import { segmentLocking, activeSegmentation, segmentIndex as segmentIndexController, config as segmentationConfig, } from '../../stateManagement/segmentation';
10
11
  import { getCurrentLabelmapImageIdForViewport, getSegmentation, } from '../../stateManagement/segmentation/segmentationState';
11
12
  import getViewportLabelmapRenderMode from '../../stateManagement/segmentation/helpers/getViewportLabelmapRenderMode';
@@ -37,7 +38,7 @@ class CircleScissorsTool extends LabelmapBaseTool {
37
38
  const enabledElement = getEnabledElement(element);
38
39
  const { viewport } = enabledElement;
39
40
  this.isDrawing = true;
40
- const camera = viewport.getCamera();
41
+ const camera = getViewportICamera(viewport);
41
42
  const { viewPlaneNormal, viewUp } = camera;
42
43
  const activeLabelmapSegmentation = activeSegmentation.getActiveSegmentation(viewport.id);
43
44
  if (!activeLabelmapSegmentation) {
@@ -7,6 +7,7 @@ import { Events, SegmentationRepresentations } from '../../enums';
7
7
  import { drawRect as drawRectSvg } from '../../drawingSvg';
8
8
  import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
9
9
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
10
+ import getViewportICamera from '../../utilities/getViewportICamera';
10
11
  import { config as segmentationConfig, segmentLocking, segmentIndex as segmentIndexController, activeSegmentation, } from '../../stateManagement/segmentation';
11
12
  import { getCurrentLabelmapImageIdForViewport, getSegmentation, } from '../../stateManagement/segmentation/segmentationState';
12
13
  import getViewportLabelmapRenderMode from '../../stateManagement/segmentation/helpers/getViewportLabelmapRenderMode';
@@ -35,7 +36,7 @@ class RectangleScissorsTool extends LabelmapBaseTool {
35
36
  const enabledElement = getEnabledElement(element);
36
37
  const { viewport } = enabledElement;
37
38
  this.isDrawing = true;
38
- const camera = viewport.getCamera();
39
+ const camera = getViewportICamera(viewport);
39
40
  const { viewPlaneNormal, viewUp } = camera;
40
41
  const activeLabelmapSegmentation = activeSegmentation.getActiveSegmentation(viewport.id);
41
42
  if (!activeLabelmapSegmentation) {
@@ -7,6 +7,7 @@ import { drawLine as drawLineSvg, drawHandles as drawHandlesSvg, } from '../../d
7
7
  import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
8
8
  import { hideElementCursor } from '../../cursors/elementCursor';
9
9
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
10
+ import getViewportICamera from '../../utilities/getViewportICamera';
10
11
  import BidirectionalTool from '../annotation/BidirectionalTool';
11
12
  import { getSegmentIndexColor } from '../../stateManagement/segmentation/config/segmentationColor';
12
13
  class SegmentBidirectionalTool extends BidirectionalTool {
@@ -118,7 +119,7 @@ class SegmentBidirectionalTool extends BidirectionalTool {
118
119
  const enabledElement = getEnabledElement(element);
119
120
  const { viewport } = enabledElement;
120
121
  this.isDrawing = true;
121
- const camera = viewport.getCamera();
122
+ const camera = getViewportICamera(viewport);
122
123
  const { viewPlaneNormal, viewUp } = camera;
123
124
  const referencedImageId = this.getReferencedImageId(viewport, worldPos, viewPlaneNormal, viewUp);
124
125
  const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
@@ -1,6 +1,7 @@
1
1
  import { vec3 } from 'gl-matrix';
2
2
  import { utilities as csUtils, StackViewport } from '@cornerstonejs/core';
3
3
  import { getBoundingBoxAroundShapeIJK } from '../../../utilities/boundingBox';
4
+ import getViewportICamera from '../../../utilities/getViewportICamera';
4
5
  import BrushStrategy from './BrushStrategy';
5
6
  import { StrategyCallbacks } from '../../../enums';
6
7
  import compositions from './compositions';
@@ -47,7 +48,7 @@ function createPointInRectangle(viewport, points, segmentationImageData) {
47
48
  vec3.normalize(normal, normal);
48
49
  const direction = segmentationImageData.getDirection();
49
50
  const spacing = segmentationImageData.getSpacing();
50
- const { viewPlaneNormal } = viewport.getCamera();
51
+ const { viewPlaneNormal } = getViewportICamera(viewport);
51
52
  const EPS = csUtils.getSpacingInNormalDirection({
52
53
  direction,
53
54
  spacing,
@@ -17,7 +17,7 @@ export type LabelmapStyle = BaseLabelmapStyle & InactiveLabelmapStyle;
17
17
  export type LabelmapLayerType = 'volume' | 'stack';
18
18
  export type LabelmapLayer = {
19
19
  labelmapId: string;
20
- type: LabelmapLayerType;
20
+ storageKind: LabelmapLayerType;
21
21
  volumeId?: string;
22
22
  geometryVolumeId?: string;
23
23
  referencedVolumeId?: string;
@@ -339,6 +339,31 @@ function _createDynamicVolumeViewportCinePlayContext(volume) {
339
339
  },
340
340
  };
341
341
  }
342
+ function _createGenericViewportCinePlayContext(viewport, waitForRendered) {
343
+ return {
344
+ get numScrollSteps() {
345
+ return viewport.getNumberOfSlices();
346
+ },
347
+ get currentStepIndex() {
348
+ return viewport.getSliceIndex();
349
+ },
350
+ get frameTimeVectorEnabled() {
351
+ return viewport.getCurrentMode() === 'stack';
352
+ },
353
+ waitForRenderedCount: 0,
354
+ scroll(delta) {
355
+ const status = viewport.viewportStatus;
356
+ if (this.waitForRenderedCount <= waitForRendered &&
357
+ status !== undefined &&
358
+ status !== ViewportStatus.RENDERED) {
359
+ this.waitForRenderedCount++;
360
+ return;
361
+ }
362
+ this.waitForRenderedCount = 0;
363
+ csUtils.scroll(viewport, { delta, debounceLoading: debounced });
364
+ },
365
+ };
366
+ }
342
367
  function _createCinePlayContext(viewport, playClipOptions) {
343
368
  if (viewport instanceof StackViewport) {
344
369
  return _createStackViewportCinePlayContext(viewport, playClipOptions.waitForRendered ?? 30);
@@ -353,6 +378,9 @@ function _createCinePlayContext(viewport, playClipOptions) {
353
378
  if (viewport instanceof VideoViewport) {
354
379
  return _createVideoViewportCinePlayContext(viewport, playClipOptions.waitForRendered ?? 30);
355
380
  }
381
+ if (csUtils.isGenericViewport(viewport)) {
382
+ return _createGenericViewportCinePlayContext(viewport, playClipOptions.waitForRendered ?? 30);
383
+ }
356
384
  throw new Error('Unknown viewport type');
357
385
  }
358
386
  export { playClip, stopClip };
@@ -0,0 +1,11 @@
1
+ import { type Types } from '@cornerstonejs/core';
2
+ export declare function getSlabThicknessOrDefault(viewport: Types.IViewport): number;
3
+ export declare function jumpToFocalPoint(viewport: Types.IViewport, cameraFocalPoint: Types.Point3): void;
4
+ export interface NativeSourceProperties {
5
+ properties: Record<string, unknown>;
6
+ rotation?: number;
7
+ flipHorizontal?: boolean;
8
+ flipVertical?: boolean;
9
+ currentImageId?: string;
10
+ }
11
+ export declare function getNativeSourceProperties(viewport: Types.IViewport): NativeSourceProperties;
@@ -0,0 +1,37 @@
1
+ import { CONSTANTS, utilities as csUtils, } from '@cornerstonejs/core';
2
+ import { getViewportPresentation } from './viewportPresentation';
3
+ const { RENDERING_DEFAULTS } = CONSTANTS;
4
+ export function getSlabThicknessOrDefault(viewport) {
5
+ if (csUtils.isGenericViewport(viewport)) {
6
+ return RENDERING_DEFAULTS.MINIMUM_SLAB_THICKNESS;
7
+ }
8
+ return viewport.getSlabThickness();
9
+ }
10
+ export function jumpToFocalPoint(viewport, cameraFocalPoint) {
11
+ if (csUtils.isGenericViewport(viewport)) {
12
+ viewport.setViewReference({ cameraFocalPoint });
13
+ }
14
+ }
15
+ export function getNativeSourceProperties(viewport) {
16
+ if (!csUtils.viewportSupportsDisplaySetPresentation(viewport)) {
17
+ return { properties: {} };
18
+ }
19
+ const sourceDataId = viewport.getSourceDataId();
20
+ const properties = {
21
+ ...((sourceDataId
22
+ ? viewport.getDisplaySetPresentation(sourceDataId)
23
+ : {}) ?? {}),
24
+ };
25
+ const voiLUTFunction = properties.VOILUTFunction ?? properties.voiLUTFunction;
26
+ if (voiLUTFunction !== undefined) {
27
+ properties.VOILUTFunction = voiLUTFunction;
28
+ }
29
+ const presentation = (getViewportPresentation(viewport) ?? {});
30
+ return {
31
+ properties,
32
+ rotation: presentation.rotation,
33
+ flipHorizontal: presentation.flipHorizontal,
34
+ flipVertical: presentation.flipVertical,
35
+ currentImageId: viewport.getCurrentImageId(),
36
+ };
37
+ }
@@ -1,12 +1,10 @@
1
1
  import { utilities as csUtils } from '@cornerstonejs/core';
2
- import { isViewportPreScaled } from './viewport';
3
2
  const DEFAULT_MULTIPLIER = 4;
4
3
  function getVOIMultipliers(viewport, volumeId, options) {
5
- const modality = csUtils.getViewportModality(viewport, volumeId);
4
+ const { modality, isPreScaled } = csUtils.getScalingDescriptor(viewport, volumeId) ?? {};
6
5
  if (modality === 'PT') {
7
6
  const { clientWidth, clientHeight } = viewport.element;
8
7
  const ptMultiplier = 5 / Math.max(clientWidth, clientHeight);
9
- const isPreScaled = isViewportPreScaled(viewport, volumeId);
10
8
  const { fixedPTWindowWidth = true } = options ?? {};
11
9
  const xMultiplier = fixedPTWindowWidth ? 0 : ptMultiplier;
12
10
  return isPreScaled
@@ -1,7 +1,8 @@
1
1
  import { utilities } from '@cornerstonejs/core';
2
2
  export default function getViewportICamera(viewport, viewReference = viewport.getViewReference()) {
3
- const genericViewport = viewport;
4
- const camera = (genericViewport.getResolvedView?.()?.toICamera?.() ||
3
+ const camera = ((utilities.isGenericViewport(viewport)
4
+ ? viewport.getResolvedView()?.toICamera()
5
+ : undefined) ||
5
6
  viewport.getCamera?.() ||
6
7
  {});
7
8
  const focalPoint = utilities.clonePoint3(viewReference?.cameraFocalPoint || camera.focalPoint);
@@ -43,4 +43,5 @@ import { moveAnnotationToViewPlane } from './moveAnnotationToViewPlane';
43
43
  import { safeStructuredClone } from './safeStructuredClone';
44
44
  import getOrCreateImageVolume from './segmentation/getOrCreateImageVolume';
45
45
  import * as usFanExtraction from '../tools/annotation/UltrasoundPleuraBLineTool/utils/fanExtraction';
46
- export { math, planar, viewportFilters, drawing, debounce, dynamicVolume, throttle, orientation, isObject, touch, triggerEvent, calibrateImageSpacing, getCalibratedLengthUnitsAndScale, getCalibratedProbeUnitsAndValue, getCalibratedAspect, getPixelValueUnits, getPixelValueUnitsImageId, segmentation, contours, triggerAnnotationRenderForViewportIds, triggerAnnotationRenderForToolGroupIds, triggerAnnotationRender, getSphereBoundsInfo, getAnnotationNearPoint, getViewportForAnnotation, getAnnotationNearPointOnEnabledElement, viewport, cine, boundingBox, draw3D, rectangleROITool, planarFreehandROITool, stackPrefetch, stackContextPrefetch, roundNumber, pointToString, polyDataUtils, voi, AnnotationMultiSlice, contourSegmentation, annotationHydration, getClosestImageIdForStackViewport, pointInSurroundingSphereCallback, normalizeViewportPlane, IslandRemoval, geometricSurfaceUtils, usFanExtraction, setAnnotationLabel, moveAnnotationToViewPlane, safeStructuredClone, getOrCreateImageVolume, };
46
+ import { jumpToFocalPoint } from './genericViewportToolHelpers';
47
+ export { math, planar, viewportFilters, drawing, debounce, dynamicVolume, throttle, orientation, isObject, touch, triggerEvent, calibrateImageSpacing, getCalibratedLengthUnitsAndScale, getCalibratedProbeUnitsAndValue, getCalibratedAspect, getPixelValueUnits, getPixelValueUnitsImageId, segmentation, contours, triggerAnnotationRenderForViewportIds, triggerAnnotationRenderForToolGroupIds, triggerAnnotationRender, getSphereBoundsInfo, getAnnotationNearPoint, getViewportForAnnotation, getAnnotationNearPointOnEnabledElement, viewport, cine, boundingBox, draw3D, rectangleROITool, planarFreehandROITool, stackPrefetch, stackContextPrefetch, roundNumber, pointToString, polyDataUtils, voi, AnnotationMultiSlice, contourSegmentation, annotationHydration, getClosestImageIdForStackViewport, pointInSurroundingSphereCallback, normalizeViewportPlane, IslandRemoval, geometricSurfaceUtils, usFanExtraction, setAnnotationLabel, moveAnnotationToViewPlane, safeStructuredClone, getOrCreateImageVolume, jumpToFocalPoint, };
@@ -43,4 +43,5 @@ import { moveAnnotationToViewPlane } from './moveAnnotationToViewPlane';
43
43
  import { safeStructuredClone } from './safeStructuredClone';
44
44
  import getOrCreateImageVolume from './segmentation/getOrCreateImageVolume';
45
45
  import * as usFanExtraction from '../tools/annotation/UltrasoundPleuraBLineTool/utils/fanExtraction';
46
- export { math, planar, viewportFilters, drawing, debounce, dynamicVolume, throttle, orientation, isObject, touch, triggerEvent, calibrateImageSpacing, getCalibratedLengthUnitsAndScale, getCalibratedProbeUnitsAndValue, getCalibratedAspect, getPixelValueUnits, getPixelValueUnitsImageId, segmentation, contours, triggerAnnotationRenderForViewportIds, triggerAnnotationRenderForToolGroupIds, triggerAnnotationRender, getSphereBoundsInfo, getAnnotationNearPoint, getViewportForAnnotation, getAnnotationNearPointOnEnabledElement, viewport, cine, boundingBox, draw3D, rectangleROITool, planarFreehandROITool, stackPrefetch, stackContextPrefetch, roundNumber, pointToString, polyDataUtils, voi, AnnotationMultiSlice, contourSegmentation, annotationHydration, getClosestImageIdForStackViewport, pointInSurroundingSphereCallback, normalizeViewportPlane, IslandRemoval, geometricSurfaceUtils, usFanExtraction, setAnnotationLabel, moveAnnotationToViewPlane, safeStructuredClone, getOrCreateImageVolume, };
46
+ import { jumpToFocalPoint } from './genericViewportToolHelpers';
47
+ export { math, planar, viewportFilters, drawing, debounce, dynamicVolume, throttle, orientation, isObject, touch, triggerEvent, calibrateImageSpacing, getCalibratedLengthUnitsAndScale, getCalibratedProbeUnitsAndValue, getCalibratedAspect, getPixelValueUnits, getPixelValueUnitsImageId, segmentation, contours, triggerAnnotationRenderForViewportIds, triggerAnnotationRenderForToolGroupIds, triggerAnnotationRender, getSphereBoundsInfo, getAnnotationNearPoint, getViewportForAnnotation, getAnnotationNearPointOnEnabledElement, viewport, cine, boundingBox, draw3D, rectangleROITool, planarFreehandROITool, stackPrefetch, stackContextPrefetch, roundNumber, pointToString, polyDataUtils, voi, AnnotationMultiSlice, contourSegmentation, annotationHydration, getClosestImageIdForStackViewport, pointInSurroundingSphereCallback, normalizeViewportPlane, IslandRemoval, geometricSurfaceUtils, usFanExtraction, setAnnotationLabel, moveAnnotationToViewPlane, safeStructuredClone, getOrCreateImageVolume, jumpToFocalPoint, };
@@ -1,11 +1,15 @@
1
- import { StackViewport } from '@cornerstonejs/core';
1
+ import { StackViewport, utilities as csUtils } from '@cornerstonejs/core';
2
2
  import { vec3 } from 'gl-matrix';
3
+ import getViewportICamera from '../../getViewportICamera';
3
4
  const EPSILON = 1e-3;
4
5
  const getSubPixelSpacingAndXYDirections = (viewport, subPixelResolution) => {
5
6
  let spacing;
6
7
  let xDir;
7
8
  let yDir;
8
- if (viewport instanceof StackViewport) {
9
+ const isGeneric = csUtils.isGenericViewport(viewport);
10
+ const isImageSlice = viewport instanceof StackViewport ||
11
+ (isGeneric && csUtils.getViewportContentMode(viewport) === 'stack');
12
+ if (isImageSlice) {
9
13
  const imageData = viewport.getImageData();
10
14
  if (!imageData) {
11
15
  return;
@@ -17,7 +21,7 @@ const getSubPixelSpacingAndXYDirections = (viewport, subPixelResolution) => {
17
21
  else {
18
22
  const imageData = viewport.getImageData();
19
23
  const { direction, spacing: volumeSpacing } = imageData;
20
- const { viewPlaneNormal, viewUp } = viewport.getCamera();
24
+ const { viewPlaneNormal, viewUp } = (isGeneric ? getViewportICamera(viewport) : viewport.getCamera());
21
25
  const iVector = direction.slice(0, 3);
22
26
  const jVector = direction.slice(3, 6);
23
27
  const kVector = direction.slice(6, 9);
@@ -1,8 +1,13 @@
1
1
  import { StackViewport, VolumeViewport, utilities as csUtils, } from '@cornerstonejs/core';
2
2
  import filterAnnotationsWithinSlice from './filterAnnotationsWithinSlice';
3
+ import getViewportICamera from '../getViewportICamera';
3
4
  export default function filterAnnotationsForDisplay(viewport, annotations, filterOptions = {}) {
4
- if (viewport instanceof VolumeViewport) {
5
- const camera = viewport.getCamera();
5
+ const isLegacyVolume = viewport instanceof VolumeViewport;
6
+ const isNativeVolume = csUtils.getViewportContentMode(viewport) === 'volume';
7
+ if (isLegacyVolume || isNativeVolume) {
8
+ const camera = isLegacyVolume
9
+ ? viewport.getCamera()
10
+ : getViewportICamera(viewport);
6
11
  const { spacingInNormalDirection } = csUtils.getTargetVolumeAndSpacingInNormalDir(viewport, camera);
7
12
  return filterAnnotationsWithinSlice(annotations, camera, spacingInNormalDirection);
8
13
  }
@@ -1,4 +1,5 @@
1
1
  import { BaseVolumeViewport, cache, utilities } from '@cornerstonejs/core';
2
+ import getViewportICamera from '../getViewportICamera';
2
3
  import { SegmentationRepresentations } from '../../enums';
3
4
  import { getSegmentation, getCurrentLabelmapImageIdsForViewport, } from '../../stateManagement/segmentation/segmentationState';
4
5
  import { getAnnotation } from '../../stateManagement';
@@ -75,7 +76,7 @@ export function getSegmentIndexAtWorldForLabelmap(segmentation, worldPoint, { vi
75
76
  export function getSegmentIndexAtWorldForContour(segmentation, worldPoint, { viewport }) {
76
77
  const contourData = segmentation.representationData.Contour;
77
78
  const segmentIndices = Array.from(contourData.annotationUIDsMap.keys());
78
- const { viewPlaneNormal } = viewport.getCamera();
79
+ const { viewPlaneNormal } = getViewportICamera(viewport);
79
80
  for (const segmentIndex of segmentIndices) {
80
81
  const annotationsSet = contourData.annotationUIDsMap.get(segmentIndex);
81
82
  if (!annotationsSet) {
@@ -1,5 +1,6 @@
1
1
  import { quat, vec3 } from 'gl-matrix';
2
2
  import { utilities as csUtils, cache, volumeLoader } from '@cornerstonejs/core';
3
+ import getViewportICamera from '../../getViewportICamera';
3
4
  import { run } from './runGrowCut';
4
5
  import { getSphereBoundsInfo } from '../../getSphereBoundsInfo';
5
6
  const { transformWorldToIndex } = csUtils;
@@ -29,7 +30,7 @@ function _getSphereBoundsInfo(referencedVolume, sphereInfo) {
29
30
  }
30
31
  function _createSubVolumeFromSphere(referencedVolume, sphereInfo, viewport) {
31
32
  const refVolImageData = referencedVolume.imageData;
32
- const camera = viewport.getCamera();
33
+ const camera = getViewportICamera(viewport);
33
34
  const { ijkVecRowDir, ijkVecColDir } = csUtils.getVolumeDirectionVectors(refVolImageData, camera);
34
35
  const obliqueView = [ijkVecRowDir, ijkVecColDir].some((vec) => !csUtils.isEqual(Math.abs(vec[0]), 1) &&
35
36
  !csUtils.isEqual(Math.abs(vec[1]), 1) &&
@@ -113,7 +114,7 @@ function _setNegativeSeedValues(subVolume, labelmap, sphereInfo, viewport, optio
113
114
  const subVolPixelData = subVolume.voxelManager.getCompleteScalarDataArray();
114
115
  const [columns, rows, numSlices] = labelmap.dimensions;
115
116
  const numPixelsPerSlice = columns * rows;
116
- const { worldVecRowDir, worldVecSliceDir } = csUtils.getVolumeDirectionVectors(labelmap.imageData, viewport.getCamera());
117
+ const { worldVecRowDir, worldVecSliceDir } = csUtils.getVolumeDirectionVectors(labelmap.imageData, getViewportICamera(viewport));
117
118
  const ijkSphereCenter = transformWorldToIndex(subVolume.imageData, sphereInfo.center);
118
119
  const referencePixelValue = subVolPixelData[ijkSphereCenter[2] * columns * rows +
119
120
  ijkSphereCenter[1] * columns +
@@ -0,0 +1,3 @@
1
+ import { type Types } from '@cornerstonejs/core';
2
+ export default function setViewportCamera(viewport: Types.IViewport, camera: Partial<Types.ICamera>): void;
3
+ export declare function resetViewportCamera(viewport: Types.IViewport, options?: unknown): void;
@@ -0,0 +1,58 @@
1
+ import { utilities, Enums } from '@cornerstonejs/core';
2
+ function normalizeVec3(v) {
3
+ const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
4
+ return length > 0 ? [v[0] / length, v[1] / length, v[2] / length] : v;
5
+ }
6
+ export default function setViewportCamera(viewport, camera) {
7
+ const vp = viewport;
8
+ if (utilities.isGenericViewport(viewport)) {
9
+ if (viewport.type === Enums.ViewportType.PLANAR_NEXT &&
10
+ typeof vp.setViewReference === 'function') {
11
+ const ref = viewport.getViewReference();
12
+ const focalPoint = camera.focalPoint ?? ref?.cameraFocalPoint;
13
+ const viewUp = camera.viewUp ?? ref?.viewUp;
14
+ let viewPlaneNormal = camera.viewPlaneNormal;
15
+ if (!viewPlaneNormal && camera.position && focalPoint) {
16
+ viewPlaneNormal = normalizeVec3([
17
+ camera.position[0] - focalPoint[0],
18
+ camera.position[1] - focalPoint[1],
19
+ camera.position[2] - focalPoint[2],
20
+ ]);
21
+ }
22
+ const rotatingVp = viewport;
23
+ const readResolvedScale = () => rotatingVp.getResolvedView?.()?.toICamera?.()?.parallelScale;
24
+ const beforeScale = readResolvedScale();
25
+ const beforeZoom = rotatingVp.getZoom?.();
26
+ vp.setViewReference({
27
+ ...ref,
28
+ cameraFocalPoint: focalPoint,
29
+ viewPlaneNormal,
30
+ viewUp,
31
+ });
32
+ rotatingVp.invalidateResolvedView?.();
33
+ const afterScale = readResolvedScale();
34
+ if (beforeScale &&
35
+ afterScale &&
36
+ typeof beforeZoom === 'number' &&
37
+ typeof rotatingVp.setZoom === 'function') {
38
+ const newZoom = (afterScale * beforeZoom) / beforeScale;
39
+ if (Number.isFinite(newZoom) && newZoom > 0) {
40
+ rotatingVp.setZoom(newZoom);
41
+ }
42
+ }
43
+ viewport.render();
44
+ return;
45
+ }
46
+ vp.setViewState?.(camera);
47
+ return;
48
+ }
49
+ vp.setCamera?.(camera);
50
+ }
51
+ export function resetViewportCamera(viewport, options) {
52
+ const vp = viewport;
53
+ if (utilities.isGenericViewport(viewport)) {
54
+ vp.resetViewState?.(options);
55
+ return;
56
+ }
57
+ vp.resetCamera?.(options);
58
+ }