@cornerstonejs/tools 5.4.17 → 5.5.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 (49) hide show
  1. package/dist/esm/enums/Events.d.ts +7 -0
  2. package/dist/esm/enums/Events.js +7 -0
  3. package/dist/esm/index.d.ts +2 -2
  4. package/dist/esm/index.js +2 -2
  5. package/dist/esm/tools/SliceIntersectionTool.d.ts +150 -0
  6. package/dist/esm/tools/SliceIntersectionTool.js +1065 -0
  7. package/dist/esm/tools/WorldCrosshairTool.d.ts +118 -0
  8. package/dist/esm/tools/WorldCrosshairTool.js +749 -0
  9. package/dist/esm/tools/annotation/LivewireContourTool.d.ts +1 -3
  10. package/dist/esm/tools/annotation/LivewireContourTool.js +0 -18
  11. package/dist/esm/tools/annotation/SplineROITool.d.ts +1 -3
  12. package/dist/esm/tools/annotation/SplineROITool.js +0 -18
  13. package/dist/esm/tools/base/AnnotationTool.d.ts +4 -0
  14. package/dist/esm/tools/base/AnnotationTool.js +20 -0
  15. package/dist/esm/tools/index.d.ts +3 -1
  16. package/dist/esm/tools/index.js +3 -1
  17. package/dist/esm/tools/worldCrosshair/WorldCrosshairLines3D.d.ts +8 -0
  18. package/dist/esm/tools/worldCrosshair/WorldCrosshairLines3D.js +81 -0
  19. package/dist/esm/utilities/genericViewportToolHelpers.d.ts +1 -0
  20. package/dist/esm/utilities/genericViewportToolHelpers.js +31 -0
  21. package/dist/esm/utilities/index.d.ts +3 -1
  22. package/dist/esm/utilities/index.js +3 -1
  23. package/dist/esm/utilities/pickIntensityPointInSlab.d.ts +7 -0
  24. package/dist/esm/utilities/pickIntensityPointInSlab.js +106 -0
  25. package/dist/esm/utilities/spatial/areViewportsSpatiallyLinked.d.ts +3 -0
  26. package/dist/esm/utilities/spatial/areViewportsSpatiallyLinked.js +48 -0
  27. package/dist/esm/utilities/spatial/clipWorldLineToViewportCanvas.d.ts +3 -0
  28. package/dist/esm/utilities/spatial/clipWorldLineToViewportCanvas.js +58 -0
  29. package/dist/esm/utilities/spatial/distancePointToPlane.d.ts +3 -0
  30. package/dist/esm/utilities/spatial/distancePointToPlane.js +6 -0
  31. package/dist/esm/utilities/spatial/getDisplayedCanvasSize.d.ts +5 -0
  32. package/dist/esm/utilities/spatial/getDisplayedCanvasSize.js +12 -0
  33. package/dist/esm/utilities/spatial/getViewportPlane.d.ts +3 -0
  34. package/dist/esm/utilities/spatial/getViewportPlane.js +33 -0
  35. package/dist/esm/utilities/spatial/index.d.ts +11 -0
  36. package/dist/esm/utilities/spatial/index.js +10 -0
  37. package/dist/esm/utilities/spatial/intersectPlanes.d.ts +2 -0
  38. package/dist/esm/utilities/spatial/intersectPlanes.js +30 -0
  39. package/dist/esm/utilities/spatial/projectPointToPlane.d.ts +3 -0
  40. package/dist/esm/utilities/spatial/projectPointToPlane.js +11 -0
  41. package/dist/esm/utilities/spatial/rotateViewportAroundWorldPoint.d.ts +2 -0
  42. package/dist/esm/utilities/spatial/rotateViewportAroundWorldPoint.js +34 -0
  43. package/dist/esm/utilities/spatial/translateViewportAlongNormal.d.ts +2 -0
  44. package/dist/esm/utilities/spatial/translateViewportAlongNormal.js +36 -0
  45. package/dist/esm/utilities/spatial/types.d.ts +19 -0
  46. package/dist/esm/utilities/spatial/types.js +0 -0
  47. package/dist/esm/version.d.ts +1 -1
  48. package/dist/esm/version.js +1 -1
  49. package/package.json +4 -4
@@ -42,10 +42,8 @@ declare class LivewireContourTool extends ContourSegmentationBaseTool {
42
42
  private _mouseDownCallback;
43
43
  private _mouseMoveCallback;
44
44
  editHandle(worldPos: Types.Point3, element: any, annotation: LivewireContourAnnotation, handleIndex: number): void;
45
- private _dragCallback;
45
+ protected _dragCallback: (evt: EventTypes.InteractionEventType) => void;
46
46
  cancel: (element: HTMLDivElement) => string;
47
- private _activateModify;
48
- private _deactivateModify;
49
47
  private _activateDraw;
50
48
  private _deactivateDraw;
51
49
  renderAnnotation(enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper): boolean;
@@ -268,24 +268,6 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
268
268
  this.scissors = null;
269
269
  return annotation.annotationUID;
270
270
  };
271
- this._activateModify = (element) => {
272
- state.isInteractingWithTool = true;
273
- element.addEventListener(Events.MOUSE_UP, this._endCallback);
274
- element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
275
- element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
276
- element.addEventListener(Events.TOUCH_END, this._endCallback);
277
- element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
278
- element.addEventListener(Events.TOUCH_TAP, this._endCallback);
279
- };
280
- this._deactivateModify = (element) => {
281
- state.isInteractingWithTool = false;
282
- element.removeEventListener(Events.MOUSE_UP, this._endCallback);
283
- element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
284
- element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
285
- element.removeEventListener(Events.TOUCH_END, this._endCallback);
286
- element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
287
- element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
288
- };
289
271
  this._activateDraw = (element) => {
290
272
  state.isInteractingWithTool = true;
291
273
  element.addEventListener(Events.MOUSE_MOVE, this._mouseMoveCallback);
@@ -52,13 +52,11 @@ declare class SplineROITool extends ContourSegmentationBaseTool {
52
52
  private _keyDownCallback;
53
53
  private _mouseMoveCallback;
54
54
  private _mouseDownCallback;
55
- private _dragCallback;
55
+ protected _dragCallback: (evt: EventTypes.InteractionEventType) => void;
56
56
  cancel(element: HTMLDivElement): string;
57
57
  triggerAnnotationCompleted: (annotation: SplineROIAnnotation, contourHoleProcessingEnabled: boolean) => void;
58
58
  triggerAnnotationModified: (annotation: SplineROIAnnotation, enabledElement: Types.IEnabledElement, changeType?: ChangeTypes) => void;
59
59
  triggerChangeEvent: (annotation: SplineROIAnnotation, enabledElement: Types.IEnabledElement, changeType: ChangeTypes, contourHoleProcessingEnabled: any) => void;
60
- private _activateModify;
61
- private _deactivateModify;
62
60
  private _activateDraw;
63
61
  private _deactivateDraw;
64
62
  protected isContourSegmentationTool(): boolean;
@@ -336,24 +336,6 @@ class SplineROITool extends ContourSegmentationBaseTool {
336
336
  this.triggerAnnotationModified(annotation, enabledElement, changeType);
337
337
  }
338
338
  };
339
- this._activateModify = (element) => {
340
- state.isInteractingWithTool = true;
341
- element.addEventListener(Events.MOUSE_UP, this._endCallback);
342
- element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
343
- element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
344
- element.addEventListener(Events.TOUCH_END, this._endCallback);
345
- element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
346
- element.addEventListener(Events.TOUCH_TAP, this._endCallback);
347
- };
348
- this._deactivateModify = (element) => {
349
- state.isInteractingWithTool = false;
350
- element.removeEventListener(Events.MOUSE_UP, this._endCallback);
351
- element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
352
- element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
353
- element.removeEventListener(Events.TOUCH_END, this._endCallback);
354
- element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
355
- element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
356
- };
357
339
  this._activateDraw = (element) => {
358
340
  state.isInteractingWithTool = true;
359
341
  element.addEventListener(Events.KEY_DOWN, this._keyDownCallback);
@@ -28,6 +28,10 @@ declare abstract class AnnotationTool extends AnnotationDisplayTool {
28
28
  abstract handleSelectedCallback(evt: EventTypes.InteractionEventType, annotation: Annotation, handle: ToolHandle, interactionType: InteractionTypes): void;
29
29
  abstract toolSelectedCallback(evt: EventTypes.InteractionEventType, annotation: Annotation, interactionType: InteractionTypes, canvasCoords?: Types.Point2): void;
30
30
  abstract isPointNearTool(element: HTMLDivElement, annotation: Annotation, canvasCoords: Types.Point2, proximity: number, interactionType: string): boolean;
31
+ protected _dragCallback?: (evt: any) => void;
32
+ protected _endCallback?: (evt: any) => void;
33
+ protected _activateModify: (element: HTMLDivElement) => void;
34
+ protected _deactivateModify: (element: HTMLDivElement) => void;
31
35
  mouseMoveCallback: (evt: EventTypes.MouseMoveEventType, filteredAnnotations?: Annotations) => boolean;
32
36
  getHandleNearImagePoint(element: HTMLDivElement, annotation: Annotation, canvasCoords: Types.Point2, proximity: number): ToolHandle | undefined;
33
37
  getLinkedTextBoxStyle(specifications: StyleSpecifier, annotation?: Annotation): Record<string, unknown>;
@@ -5,6 +5,8 @@ import { isAnnotationLocked } from '../../stateManagement/annotation/annotationL
5
5
  import { isAnnotationVisible } from '../../stateManagement/annotation/annotationVisibility.js';
6
6
  import { addAnnotation, removeAnnotation, getAnnotation, } from '../../stateManagement/annotation/annotationState.js';
7
7
  import { triggerAnnotationModified } from '../../stateManagement/annotation/helpers/state.js';
8
+ import { state } from '../../store/state.js';
9
+ import { Events } from '../../enums/index.js';
8
10
  import { drawLinkedTextBox } from '../../drawingSvg/index.js';
9
11
  import { getTextBoxCoordsCanvas } from '../../utilities/drawing/index.js';
10
12
  import ChangeTypes from '../../enums/ChangeTypes.js';
@@ -24,6 +26,24 @@ class AnnotationTool extends AnnotationDisplayTool {
24
26
  }
25
27
  constructor(toolProps, defaultToolProps) {
26
28
  super(toolProps, defaultToolProps);
29
+ this._activateModify = (element) => {
30
+ state.isInteractingWithTool = true;
31
+ element.addEventListener(Events.MOUSE_UP, this._endCallback);
32
+ element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
33
+ element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
34
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
35
+ element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
36
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
37
+ };
38
+ this._deactivateModify = (element) => {
39
+ state.isInteractingWithTool = false;
40
+ element.removeEventListener(Events.MOUSE_UP, this._endCallback);
41
+ element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
42
+ element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
43
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
44
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
45
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
46
+ };
27
47
  this.mouseMoveCallback = (evt, filteredAnnotations) => {
28
48
  if (!filteredAnnotations) {
29
49
  return false;
@@ -10,6 +10,8 @@ import PlanarRotateTool from './PlanarRotateTool.js';
10
10
  import ZoomTool from './ZoomTool.js';
11
11
  import MIPJumpToClickTool from './MIPJumpToClickTool.js';
12
12
  import CrosshairsTool from './CrosshairsTool.js';
13
+ import WorldCrosshairTool from './WorldCrosshairTool.js';
14
+ import SliceIntersectionTool from './SliceIntersectionTool.js';
13
15
  import MagnifyTool from './MagnifyTool.js';
14
16
  import AdvancedMagnifyTool from './AdvancedMagnifyTool.js';
15
17
  import ReferenceLinesTool from './ReferenceLinesTool.js';
@@ -62,4 +64,4 @@ import SegmentBidirectionalTool from './segmentation/SegmentBidirectionalTool.js
62
64
  import * as strategies from './segmentation/strategies/index.js';
63
65
  import SegmentLabelTool from './segmentation/SegmentLabelTool.js';
64
66
  import LabelMapEditWithContourTool from './segmentation/LabelmapEditWithContour.js';
65
- export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, ZoomTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, SculptorTool, SegmentSelectTool, VolumeRotateTool, RegionSegmentTool, RegionSegmentPlusTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentBidirectionalTool, SegmentLabelTool, LabelMapEditWithContourTool, strategies, };
67
+ export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, ZoomTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, SculptorTool, SegmentSelectTool, VolumeRotateTool, RegionSegmentTool, RegionSegmentPlusTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentBidirectionalTool, SegmentLabelTool, LabelMapEditWithContourTool, strategies, };
@@ -10,6 +10,8 @@ import PlanarRotateTool from './PlanarRotateTool.js';
10
10
  import ZoomTool from './ZoomTool.js';
11
11
  import MIPJumpToClickTool from './MIPJumpToClickTool.js';
12
12
  import CrosshairsTool from './CrosshairsTool.js';
13
+ import WorldCrosshairTool from './WorldCrosshairTool.js';
14
+ import SliceIntersectionTool from './SliceIntersectionTool.js';
13
15
  import MagnifyTool from './MagnifyTool.js';
14
16
  import AdvancedMagnifyTool from './AdvancedMagnifyTool.js';
15
17
  import ReferenceLinesTool from './ReferenceLinesTool.js';
@@ -62,4 +64,4 @@ import SegmentBidirectionalTool from './segmentation/SegmentBidirectionalTool.js
62
64
  import * as strategies from './segmentation/strategies/index.js';
63
65
  import SegmentLabelTool from './segmentation/SegmentLabelTool.js';
64
66
  import LabelMapEditWithContourTool from './segmentation/LabelmapEditWithContour.js';
65
- export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, ZoomTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, SculptorTool, SegmentSelectTool, VolumeRotateTool, RegionSegmentTool, RegionSegmentPlusTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentBidirectionalTool, SegmentLabelTool, LabelMapEditWithContourTool, strategies, };
67
+ export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, VolumeCroppingTool, VolumeCroppingControlTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, ZoomTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, WorldCrosshairTool, SliceIntersectionTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LabelTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, ETDRSGridTool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, UltrasoundPleuraBLineTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, OrientationControllerTool, SculptorTool, SegmentSelectTool, VolumeRotateTool, RegionSegmentTool, RegionSegmentPlusTool, ClickSegmentTool, WholeBodySegmentTool, LabelmapBaseTool, SegmentBidirectionalTool, SegmentLabelTool, LabelMapEditWithContourTool, strategies, };
@@ -0,0 +1,8 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ export type WorldCrosshairLines3DOptions = {
3
+ lineLengthMm: number;
4
+ color: [number, number, number];
5
+ uidPrefix: string;
6
+ };
7
+ export declare function updateWorldCrosshairLines3D(viewport: Types.IViewport, worldPoint: Types.Point3, options: WorldCrosshairLines3DOptions): void;
8
+ export declare function removeWorldCrosshairLines3D(viewport: Types.IViewport, uidPrefix: string): void;
@@ -0,0 +1,81 @@
1
+ import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor.js';
2
+ import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper.js';
3
+ import vtkPolyData from '@kitware/vtk.js/Common/DataModel/PolyData.js';
4
+ import vtkPoints from '@kitware/vtk.js/Common/Core/Points.js';
5
+ import vtkCellArray from '@kitware/vtk.js/Common/Core/CellArray.js';
6
+ const linesByKey = new Map();
7
+ function getKey(viewport, uidPrefix) {
8
+ return `${viewport.id}:${uidPrefix}`;
9
+ }
10
+ function setCrosshairLinePoints(polyData, worldPoint, halfLength) {
11
+ const [x, y, z] = worldPoint;
12
+ const points = vtkPoints.newInstance();
13
+ points.setNumberOfPoints(6);
14
+ points.setPoint(0, x - halfLength, y, z);
15
+ points.setPoint(1, x + halfLength, y, z);
16
+ points.setPoint(2, x, y - halfLength, z);
17
+ points.setPoint(3, x, y + halfLength, z);
18
+ points.setPoint(4, x, y, z - halfLength);
19
+ points.setPoint(5, x, y, z + halfLength);
20
+ const lines = vtkCellArray.newInstance({
21
+ values: [2, 0, 1, 2, 2, 3, 2, 4, 5],
22
+ });
23
+ polyData.setPoints(points);
24
+ polyData.setLines(lines);
25
+ polyData.modified();
26
+ }
27
+ function getViewportRenderer(viewport) {
28
+ return typeof viewport?.getRenderer === 'function'
29
+ ? viewport.getRenderer()
30
+ : undefined;
31
+ }
32
+ export function updateWorldCrosshairLines3D(viewport, worldPoint, options) {
33
+ const renderer = getViewportRenderer(viewport);
34
+ if (!renderer || !worldPoint) {
35
+ return;
36
+ }
37
+ const { lineLengthMm, color, uidPrefix } = options;
38
+ const halfLength = Math.max(lineLengthMm / 2, 1);
39
+ const key = getKey(viewport, uidPrefix);
40
+ let entry = linesByKey.get(key);
41
+ const isAttached = entry && !!renderer.getActors?.().includes(entry.actor);
42
+ if (!entry || !isAttached) {
43
+ if (entry) {
44
+ renderer.removeActor(entry.actor);
45
+ }
46
+ const polyData = vtkPolyData.newInstance();
47
+ setCrosshairLinePoints(polyData, worldPoint, halfLength);
48
+ const mapper = vtkMapper.newInstance();
49
+ mapper.setInputData(polyData);
50
+ const actor = vtkActor.newInstance();
51
+ actor.setMapper(mapper);
52
+ actor.getProperty().setColor(...color);
53
+ actor.getProperty().setLineWidth(1.5);
54
+ actor.getProperty().setInterpolationToFlat();
55
+ actor.getProperty().setAmbient(1.0);
56
+ actor.getProperty().setDiffuse(0.0);
57
+ actor.getProperty().setSpecular(0.0);
58
+ renderer.addActor(actor);
59
+ renderer.resetCameraClippingRange?.();
60
+ entry = { actor, polyData };
61
+ linesByKey.set(key, entry);
62
+ }
63
+ else {
64
+ setCrosshairLinePoints(entry.polyData, worldPoint, halfLength);
65
+ }
66
+ viewport.render();
67
+ }
68
+ export function removeWorldCrosshairLines3D(viewport, uidPrefix) {
69
+ const key = getKey(viewport, uidPrefix);
70
+ const entry = linesByKey.get(key);
71
+ linesByKey.delete(key);
72
+ if (!entry) {
73
+ return;
74
+ }
75
+ const renderer = getViewportRenderer(viewport);
76
+ if (!renderer) {
77
+ return;
78
+ }
79
+ renderer.removeActor(entry.actor);
80
+ viewport.render();
81
+ }
@@ -1,6 +1,7 @@
1
1
  import { type Types } from '@cornerstonejs/core';
2
2
  export declare function getSlabThicknessOrDefault(viewport: Types.IViewport): number;
3
3
  export declare function jumpToFocalPoint(viewport: Types.IViewport, cameraFocalPoint: Types.Point3): void;
4
+ export declare function navigatePlanarViewportToPoint(viewport: Types.IViewport, worldPoint: Types.Point3): boolean;
4
5
  export interface NativeSourceProperties {
5
6
  properties: Record<string, unknown>;
6
7
  rotation?: number;
@@ -12,6 +12,37 @@ export function jumpToFocalPoint(viewport, cameraFocalPoint) {
12
12
  viewport.setViewReference({ cameraFocalPoint });
13
13
  }
14
14
  }
15
+ export function navigatePlanarViewportToPoint(viewport, worldPoint) {
16
+ if (!csUtils.isGenericViewport(viewport)) {
17
+ return false;
18
+ }
19
+ const isStackContent = viewport.getCurrentMode?.() === 'stack';
20
+ if (isStackContent) {
21
+ const stackViewport = viewport;
22
+ if (typeof stackViewport.getImageIds !== 'function' ||
23
+ typeof stackViewport.getCurrentImageIdIndex !== 'function') {
24
+ return false;
25
+ }
26
+ let imageIndex = null;
27
+ try {
28
+ imageIndex = csUtils.getClosestStackImageIndexForPoint(worldPoint, stackViewport);
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ if (imageIndex === null || imageIndex < 0) {
34
+ return false;
35
+ }
36
+ viewport.setViewReference({
37
+ sliceIndex: imageIndex,
38
+ });
39
+ return true;
40
+ }
41
+ viewport.setViewReference({
42
+ cameraFocalPoint: [worldPoint[0], worldPoint[1], worldPoint[2]],
43
+ });
44
+ return true;
45
+ }
15
46
  export function getNativeSourceProperties(viewport) {
16
47
  if (!csUtils.viewportSupportsDisplaySetPresentation(viewport)) {
17
48
  return { properties: {} };
@@ -18,6 +18,7 @@ import * as segmentation from './segmentation/index.js';
18
18
  import * as drawing from './drawing/index.js';
19
19
  import * as math from './math/index.js';
20
20
  import * as planar from './planar/index.js';
21
+ import * as spatial from './spatial/index.js';
21
22
  import * as viewportFilters from './viewportFilters/index.js';
22
23
  import * as orientation from './orientation/index.js';
23
24
  import * as cine from './cine/index.js';
@@ -44,4 +45,5 @@ import { safeStructuredClone } from './safeStructuredClone.js';
44
45
  import getOrCreateImageVolume from './segmentation/getOrCreateImageVolume.js';
45
46
  import * as usFanExtraction from '../tools/annotation/UltrasoundPleuraBLineTool/utils/fanExtraction.js';
46
47
  import { jumpToFocalPoint } from './genericViewportToolHelpers.js';
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, };
48
+ import pickIntensityPointInSlab, { getSlabIntensityPickContext } from './pickIntensityPointInSlab.js';
49
+ export { math, planar, spatial, 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, pickIntensityPointInSlab, getSlabIntensityPickContext, };
@@ -18,6 +18,7 @@ import * as segmentation from './segmentation/index.js';
18
18
  import * as drawing from './drawing/index.js';
19
19
  import * as math from './math/index.js';
20
20
  import * as planar from './planar/index.js';
21
+ import * as spatial from './spatial/index.js';
21
22
  import * as viewportFilters from './viewportFilters/index.js';
22
23
  import * as orientation from './orientation/index.js';
23
24
  import * as cine from './cine/index.js';
@@ -44,4 +45,5 @@ import { safeStructuredClone } from './safeStructuredClone.js';
44
45
  import getOrCreateImageVolume from './segmentation/getOrCreateImageVolume.js';
45
46
  import * as usFanExtraction from '../tools/annotation/UltrasoundPleuraBLineTool/utils/fanExtraction.js';
46
47
  import { jumpToFocalPoint } from './genericViewportToolHelpers.js';
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, };
48
+ import pickIntensityPointInSlab, { getSlabIntensityPickContext, } from './pickIntensityPointInSlab.js';
49
+ export { math, planar, spatial, 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, pickIntensityPointInSlab, getSlabIntensityPickContext, };
@@ -0,0 +1,7 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ export type SlabIntensityPickMode = 'max' | 'min';
3
+ export declare function getSlabIntensityPickContext(viewport: Types.IViewport): {
4
+ mode: SlabIntensityPickMode;
5
+ slabThicknessMm: number;
6
+ } | null;
7
+ export default function pickIntensityPointInSlab(viewport: Types.IViewport, worldPoint: Types.Point3): Types.Point3 | null;
@@ -0,0 +1,106 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import { cache, Enums, utilities as csUtils, CONSTANTS, } from '@cornerstonejs/core';
3
+ import { getViewportPlane } from './spatial/index.js';
4
+ const { RENDERING_DEFAULTS } = CONSTANTS;
5
+ const STEP_SPACING_FRACTION = 0.25;
6
+ const MAX_SAMPLES = 2001;
7
+ export function getSlabIntensityPickContext(viewport) {
8
+ if (!csUtils.isGenericViewport(viewport)) {
9
+ return null;
10
+ }
11
+ try {
12
+ if (viewport.getCurrentMode?.() !== 'volume') {
13
+ return null;
14
+ }
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ if (!csUtils.viewportSupportsDisplaySetPresentation(viewport)) {
20
+ return null;
21
+ }
22
+ const dataId = viewport.getSourceDataId();
23
+ if (!dataId) {
24
+ return null;
25
+ }
26
+ const presentation = viewport.getDisplaySetPresentation(dataId);
27
+ const { blendMode, slabThickness } = presentation ?? {};
28
+ let mode;
29
+ if (blendMode === Enums.BlendModes.MAXIMUM_INTENSITY_BLEND) {
30
+ mode = 'max';
31
+ }
32
+ else if (blendMode === Enums.BlendModes.MINIMUM_INTENSITY_BLEND) {
33
+ mode = 'min';
34
+ }
35
+ else {
36
+ return null;
37
+ }
38
+ if (!Number.isFinite(slabThickness) ||
39
+ slabThickness <= RENDERING_DEFAULTS.MINIMUM_SLAB_THICKNESS) {
40
+ return null;
41
+ }
42
+ return { mode, slabThicknessMm: slabThickness };
43
+ }
44
+ export default function pickIntensityPointInSlab(viewport, worldPoint) {
45
+ const context = getSlabIntensityPickContext(viewport);
46
+ if (!context || !worldPoint) {
47
+ return null;
48
+ }
49
+ const volumeId = viewport.getVolumeId?.();
50
+ const volume = volumeId ? cache.getVolume(volumeId) : undefined;
51
+ if (!volume?.voxelManager ||
52
+ !volume.dimensions ||
53
+ !volume.spacing ||
54
+ !volume.direction ||
55
+ !volume.origin) {
56
+ return null;
57
+ }
58
+ const plane = getViewportPlane(viewport);
59
+ if (!plane) {
60
+ return null;
61
+ }
62
+ const normal = plane.normal;
63
+ const spacingAlongNormal = Math.abs(normal[0]) * volume.spacing[0] +
64
+ Math.abs(normal[1]) * volume.spacing[1] +
65
+ Math.abs(normal[2]) * volume.spacing[2];
66
+ if (!Number.isFinite(spacingAlongNormal) || spacingAlongNormal <= 0) {
67
+ return null;
68
+ }
69
+ const halfRange = context.slabThicknessMm / 2;
70
+ let step = spacingAlongNormal * STEP_SPACING_FRACTION;
71
+ const stepsPerSide = Math.min(Math.ceil(halfRange / step), (MAX_SAMPLES - 1) / 2);
72
+ step = halfRange / stepsPerSide;
73
+ const wantMax = context.mode === 'max';
74
+ let bestValue = NaN;
75
+ let bestOffset = 0;
76
+ const samplePoint = vec3.create();
77
+ const sampleAt = (offset) => {
78
+ vec3.scaleAndAdd(samplePoint, worldPoint, normal, offset);
79
+ const value = csUtils.VoxelManager.sampleAtWorld(volume, [
80
+ samplePoint[0],
81
+ samplePoint[1],
82
+ samplePoint[2],
83
+ ]);
84
+ if (!Number.isFinite(value)) {
85
+ return;
86
+ }
87
+ if (Number.isNaN(bestValue) ||
88
+ (wantMax ? value > bestValue : value < bestValue)) {
89
+ bestValue = value;
90
+ bestOffset = offset;
91
+ }
92
+ };
93
+ sampleAt(0);
94
+ for (let i = 1; i <= stepsPerSide; i++) {
95
+ sampleAt(i * step);
96
+ sampleAt(-i * step);
97
+ }
98
+ if (Number.isNaN(bestValue)) {
99
+ return null;
100
+ }
101
+ return [
102
+ worldPoint[0] + normal[0] * bestOffset,
103
+ worldPoint[1] + normal[1] * bestOffset,
104
+ worldPoint[2] + normal[2] * bestOffset,
105
+ ];
106
+ }
@@ -0,0 +1,3 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ import type { SpatialLinkOptions } from './types.js';
3
+ export default function areViewportsSpatiallyLinked(source: Types.IViewport, target: Types.IViewport, options: SpatialLinkOptions): boolean;
@@ -0,0 +1,48 @@
1
+ import { getToolGroupForViewport } from '../../store/ToolGroupManager/index.js';
2
+ function getFrameOfReferenceUID(viewport) {
3
+ return viewport.getFrameOfReferenceUID?.();
4
+ }
5
+ function hasRegistrationTransform(sourceFrameOfReferenceUID, targetFrameOfReferenceUID, options) {
6
+ const { registrationTransforms } = options;
7
+ if (!registrationTransforms?.size) {
8
+ return false;
9
+ }
10
+ return (registrationTransforms.has(`${sourceFrameOfReferenceUID}:${targetFrameOfReferenceUID}`) ||
11
+ registrationTransforms.has(`${targetFrameOfReferenceUID}:${sourceFrameOfReferenceUID}`));
12
+ }
13
+ export default function areViewportsSpatiallyLinked(source, target, options) {
14
+ if (!source || !target || !options?.policy) {
15
+ return false;
16
+ }
17
+ switch (options.policy) {
18
+ case 'frameOfReferenceUID': {
19
+ const sourceFrameOfReferenceUID = getFrameOfReferenceUID(source);
20
+ const targetFrameOfReferenceUID = getFrameOfReferenceUID(target);
21
+ if (!sourceFrameOfReferenceUID || !targetFrameOfReferenceUID) {
22
+ return false;
23
+ }
24
+ if (sourceFrameOfReferenceUID === targetFrameOfReferenceUID) {
25
+ return true;
26
+ }
27
+ return hasRegistrationTransform(sourceFrameOfReferenceUID, targetFrameOfReferenceUID, options);
28
+ }
29
+ case 'explicit': {
30
+ const { explicitLinks } = options;
31
+ if (!explicitLinks?.length) {
32
+ return false;
33
+ }
34
+ return explicitLinks.some((link) => (link.sourceViewportId === source.id &&
35
+ link.targetViewportId === target.id) ||
36
+ (link.sourceViewportId === target.id &&
37
+ link.targetViewportId === source.id));
38
+ }
39
+ case 'toolGroup': {
40
+ const sourceToolGroup = getToolGroupForViewport(source.id, source.renderingEngineId);
41
+ const targetToolGroup = getToolGroupForViewport(target.id, target.renderingEngineId);
42
+ return (!!sourceToolGroup && !!targetToolGroup &&
43
+ sourceToolGroup.id === targetToolGroup.id);
44
+ }
45
+ default:
46
+ return false;
47
+ }
48
+ }
@@ -0,0 +1,3 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ import type { WorldLine } from './types.js';
3
+ export default function clipWorldLineToViewportCanvas(line: WorldLine, viewport: Types.IVolumeViewport | Types.IViewport): [Types.Point2, Types.Point2] | null;
@@ -0,0 +1,58 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import liangBarksyClip from '../math/vec2/liangBarksyClip.js';
3
+ import getDisplayedCanvasSize from './getDisplayedCanvasSize.js';
4
+ const MIN_SEGMENT_LENGTH = 1e-3;
5
+ export default function clipWorldLineToViewportCanvas(line, viewport) {
6
+ if (!line || !viewport) {
7
+ return null;
8
+ }
9
+ const { clientWidth, clientHeight } = getDisplayedCanvasSize(viewport);
10
+ if (!clientWidth || !clientHeight) {
11
+ return null;
12
+ }
13
+ const direction = vec3.normalize(vec3.create(), line.direction);
14
+ if (vec3.length(direction) < 1e-10) {
15
+ return null;
16
+ }
17
+ const centerWorld = viewport.canvasToWorld([
18
+ clientWidth / 2,
19
+ clientHeight / 2,
20
+ ]);
21
+ const toCenter = vec3.subtract(vec3.create(), centerWorld, line.point);
22
+ const projectedLength = vec3.dot(toCenter, direction);
23
+ const anchor = vec3.scaleAndAdd(vec3.create(), line.point, direction, projectedLength);
24
+ const cornerWorld = viewport.canvasToWorld([0, 0]);
25
+ const halfDiagonalWorld = vec3.distance(centerWorld, cornerWorld);
26
+ const halfLength = Math.max(halfDiagonalWorld * 2, 1);
27
+ const startWorld = vec3.scaleAndAdd(vec3.create(), anchor, direction, -halfLength);
28
+ const endWorld = vec3.scaleAndAdd(vec3.create(), anchor, direction, halfLength);
29
+ const startCanvas = viewport.worldToCanvas([
30
+ startWorld[0],
31
+ startWorld[1],
32
+ startWorld[2],
33
+ ]);
34
+ const endCanvas = viewport.worldToCanvas([
35
+ endWorld[0],
36
+ endWorld[1],
37
+ endWorld[2],
38
+ ]);
39
+ if (![...startCanvas, ...endCanvas].every((component) => Number.isFinite(component))) {
40
+ return null;
41
+ }
42
+ const clippedStart = [startCanvas[0], startCanvas[1]];
43
+ const clippedEnd = [endCanvas[0], endCanvas[1]];
44
+ const inside = liangBarksyClip(clippedStart, clippedEnd, [
45
+ 0,
46
+ 0,
47
+ clientWidth,
48
+ clientHeight,
49
+ ]);
50
+ if (inside !== 1) {
51
+ return null;
52
+ }
53
+ const segmentLength = Math.hypot(clippedEnd[0] - clippedStart[0], clippedEnd[1] - clippedStart[1]);
54
+ if (segmentLength < MIN_SEGMENT_LENGTH) {
55
+ return null;
56
+ }
57
+ return [clippedStart, clippedEnd];
58
+ }
@@ -0,0 +1,3 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ import type { Plane } from './types.js';
3
+ export default function distancePointToPlane(point: Types.Point3, plane: Plane): number;
@@ -0,0 +1,6 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ export default function distancePointToPlane(point, plane) {
3
+ const normal = vec3.normalize(vec3.create(), plane.normal);
4
+ const toPoint = vec3.subtract(vec3.create(), point, plane.point);
5
+ return vec3.dot(toPoint, normal);
6
+ }
@@ -0,0 +1,5 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ export default function getDisplayedCanvasSize(viewport: Types.IViewport): {
3
+ clientWidth: number;
4
+ clientHeight: number;
5
+ };
@@ -0,0 +1,12 @@
1
+ import { utilities as csUtils } from '@cornerstonejs/core';
2
+ export default function getDisplayedCanvasSize(viewport) {
3
+ if (csUtils.isGenericViewport(viewport)) {
4
+ const { element } = viewport;
5
+ return {
6
+ clientWidth: element?.clientWidth ?? 0,
7
+ clientHeight: element?.clientHeight ?? 0,
8
+ };
9
+ }
10
+ const { clientWidth = 0, clientHeight = 0 } = viewport.canvas ?? {};
11
+ return { clientWidth, clientHeight };
12
+ }
@@ -0,0 +1,3 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ import type { Plane } from './types.js';
3
+ export default function getViewportPlane(viewport: Types.IViewport): Plane | null;