@cornerstonejs/core 0.36.3 → 0.36.4
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.
- package/dist/cjs/utilities/getSliceRange.js +2 -1
- package/dist/cjs/utilities/getSliceRange.js.map +1 -1
- package/dist/esm/utilities/getSliceRange.js +2 -1
- package/dist/esm/utilities/getSliceRange.js.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +4 -3
- package/src/RenderingEngine/BaseVolumeViewport.ts +847 -0
- package/src/RenderingEngine/RenderingEngine.ts +1364 -0
- package/src/RenderingEngine/StackViewport.ts +2690 -0
- package/src/RenderingEngine/Viewport.ts +1244 -0
- package/src/RenderingEngine/VolumeViewport.ts +420 -0
- package/src/RenderingEngine/VolumeViewport3D.ts +42 -0
- package/src/RenderingEngine/getRenderingEngine.ts +34 -0
- package/src/RenderingEngine/helpers/addVolumesToViewports.ts +52 -0
- package/src/RenderingEngine/helpers/cpuFallback/colors/colormap.ts +343 -0
- package/src/RenderingEngine/helpers/cpuFallback/colors/index.ts +4 -0
- package/src/RenderingEngine/helpers/cpuFallback/colors/lookupTable.ts +469 -0
- package/src/RenderingEngine/helpers/cpuFallback/drawImageSync.ts +58 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/calculateTransform.ts +136 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/canvasToPixel.ts +25 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/computeAutoVoi.ts +47 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/correctShift.ts +38 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/createViewport.ts +64 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/doesImageNeedToBeRendered.ts +36 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/fitToWindow.ts +22 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/generateColorLUT.ts +60 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/generateLut.ts +83 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/getDefaultViewport.ts +88 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/getImageFitScale.ts +52 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/getImageSize.ts +55 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/getLut.ts +53 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/getModalityLut.ts +55 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/getTransform.ts +17 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/getVOILut.ts +74 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/initializeRenderCanvas.ts +37 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/lutMatches.ts +21 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/now.ts +13 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/pixelToCanvas.ts +22 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/renderColorImage.ts +193 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/renderGrayscaleImage.ts +166 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.ts +203 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/resetCamera.ts +32 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/resize.ts +109 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/saveLastRendered.ts +36 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/setDefaultViewport.ts +17 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/setToPixelCoordinateSystem.ts +32 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/storedColorPixelDataToCanvasImageData.ts +58 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageData.ts +76 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataColorLUT.ts +60 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataPET.ts +50 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataPseudocolorLUT.ts +66 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataPseudocolorLUTPET.ts +68 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataRGBA.ts +81 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/storedRGBAPixelDataToCanvasImageData.ts +56 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/transform.ts +126 -0
- package/src/RenderingEngine/helpers/cpuFallback/rendering/validator.ts +31 -0
- package/src/RenderingEngine/helpers/createVolumeActor.ts +103 -0
- package/src/RenderingEngine/helpers/createVolumeMapper.ts +37 -0
- package/src/RenderingEngine/helpers/getOrCreateCanvas.ts +58 -0
- package/src/RenderingEngine/helpers/index.ts +15 -0
- package/src/RenderingEngine/helpers/isRgbaSourceRgbDest.ts +1 -0
- package/src/RenderingEngine/helpers/setDefaultVolumeVOI.ts +227 -0
- package/src/RenderingEngine/helpers/setVolumesForViewports.ts +52 -0
- package/src/RenderingEngine/helpers/viewportTypeToViewportClass.ts +14 -0
- package/src/RenderingEngine/helpers/viewportTypeUsesCustomRenderingPipeline.ts +7 -0
- package/src/RenderingEngine/helpers/volumeNewImageEventDispatcher.ts +75 -0
- package/src/RenderingEngine/index.ts +23 -0
- package/src/RenderingEngine/renderingEngineCache.ts +43 -0
- package/src/RenderingEngine/vtkClasses/index.js +11 -0
- package/src/RenderingEngine/vtkClasses/vtkOffscreenMultiRenderWindow.js +149 -0
- package/src/RenderingEngine/vtkClasses/vtkSharedVolumeMapper.js +52 -0
- package/src/RenderingEngine/vtkClasses/vtkSlabCamera.d.ts +781 -0
- package/src/RenderingEngine/vtkClasses/vtkSlabCamera.js +155 -0
- package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLRenderWindow.js +47 -0
- package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLTexture.js +272 -0
- package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLViewNodeFactory.js +159 -0
- package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLVolumeMapper.js +319 -0
- package/src/Settings.ts +294 -0
- package/src/cache/cache.ts +854 -0
- package/src/cache/classes/Contour.ts +70 -0
- package/src/cache/classes/ContourSet.ts +151 -0
- package/src/cache/classes/ImageVolume.ts +155 -0
- package/src/cache/index.ts +5 -0
- package/src/constants/cpuColormaps.ts +1537 -0
- package/src/constants/epsilon.ts +3 -0
- package/src/constants/index.ts +13 -0
- package/src/constants/mprCameraValues.ts +20 -0
- package/src/constants/rendering.ts +8 -0
- package/src/constants/viewportPresets.ts +357 -0
- package/src/enums/BlendModes.ts +23 -0
- package/src/enums/ContourType.ts +6 -0
- package/src/enums/Events.ts +196 -0
- package/src/enums/GeometryType.ts +5 -0
- package/src/enums/InterpolationType.ts +13 -0
- package/src/enums/OrientationAxis.ts +8 -0
- package/src/enums/RequestType.ts +13 -0
- package/src/enums/SharedArrayBufferModes.ts +11 -0
- package/src/enums/VOILUTFunctionType.ts +10 -0
- package/src/enums/ViewportType.ts +21 -0
- package/src/enums/index.ts +23 -0
- package/src/eventTarget.ts +67 -0
- package/src/getEnabledElement.ts +105 -0
- package/src/global.ts +8 -0
- package/src/index.ts +123 -0
- package/src/init.ts +247 -0
- package/src/loaders/geometryLoader.ts +108 -0
- package/src/loaders/imageLoader.ts +298 -0
- package/src/loaders/volumeLoader.ts +477 -0
- package/src/metaData.ts +84 -0
- package/src/requestPool/imageLoadPoolManager.ts +43 -0
- package/src/requestPool/imageRetrievalPoolManager.ts +25 -0
- package/src/requestPool/requestPoolManager.ts +329 -0
- package/src/types/ActorSliceRange.ts +17 -0
- package/src/types/CPUFallbackColormap.ts +23 -0
- package/src/types/CPUFallbackColormapData.ts +12 -0
- package/src/types/CPUFallbackColormapsData.ts +7 -0
- package/src/types/CPUFallbackEnabledElement.ts +71 -0
- package/src/types/CPUFallbackLUT.ts +5 -0
- package/src/types/CPUFallbackLookupTable.ts +17 -0
- package/src/types/CPUFallbackRenderingTools.ts +25 -0
- package/src/types/CPUFallbackTransform.ts +16 -0
- package/src/types/CPUFallbackViewport.ts +29 -0
- package/src/types/CPUFallbackViewportDisplayedArea.ts +15 -0
- package/src/types/CPUIImageData.ts +47 -0
- package/src/types/ContourData.ts +19 -0
- package/src/types/Cornerstone3DConfig.ts +31 -0
- package/src/types/CustomEventType.ts +14 -0
- package/src/types/EventTypes.ts +403 -0
- package/src/types/FlipDirection.ts +9 -0
- package/src/types/IActor.ts +23 -0
- package/src/types/ICache.ts +28 -0
- package/src/types/ICachedGeometry.ts +13 -0
- package/src/types/ICachedImage.ts +13 -0
- package/src/types/ICachedVolume.ts +12 -0
- package/src/types/ICamera.ts +36 -0
- package/src/types/IContour.ts +18 -0
- package/src/types/IContourSet.ts +56 -0
- package/src/types/IDynamicImageVolume.ts +18 -0
- package/src/types/IEnabledElement.ts +21 -0
- package/src/types/IGeometry.ts +12 -0
- package/src/types/IImage.ts +113 -0
- package/src/types/IImageData.ts +45 -0
- package/src/types/IImageVolume.ts +78 -0
- package/src/types/ILoadObject.ts +36 -0
- package/src/types/IRegisterImageLoader.ts +10 -0
- package/src/types/IRenderingEngine.ts +28 -0
- package/src/types/IStackViewport.ts +138 -0
- package/src/types/IStreamingImageVolume.ts +13 -0
- package/src/types/IStreamingVolumeProperties.ts +14 -0
- package/src/types/IViewport.ts +149 -0
- package/src/types/IViewportId.ts +9 -0
- package/src/types/IVolume.ts +45 -0
- package/src/types/IVolumeInput.ts +36 -0
- package/src/types/IVolumeViewport.ts +141 -0
- package/src/types/ImageLoaderFn.ts +16 -0
- package/src/types/ImageSliceData.ts +6 -0
- package/src/types/Mat3.ts +16 -0
- package/src/types/Metadata.ts +39 -0
- package/src/types/OrientationVectors.ts +36 -0
- package/src/types/Plane.ts +6 -0
- package/src/types/Point2.ts +6 -0
- package/src/types/Point3.ts +6 -0
- package/src/types/Point4.ts +6 -0
- package/src/types/ScalingParameters.ts +27 -0
- package/src/types/StackViewportProperties.ts +25 -0
- package/src/types/TransformMatrix2D.ts +4 -0
- package/src/types/ViewportInputOptions.ts +21 -0
- package/src/types/ViewportPreset.ts +14 -0
- package/src/types/VolumeLoaderFn.ts +18 -0
- package/src/types/VolumeViewportProperties.ts +14 -0
- package/src/types/index.ts +157 -0
- package/src/types/voi.ts +15 -0
- package/src/utilities/actorCheck.ts +24 -0
- package/src/utilities/applyPreset.ts +132 -0
- package/src/utilities/calculateViewportsSpatialRegistration.ts +74 -0
- package/src/utilities/calibratedPixelSpacingMetadataProvider.ts +38 -0
- package/src/utilities/createFloat32SharedArray.ts +45 -0
- package/src/utilities/createInt16SharedArray.ts +43 -0
- package/src/utilities/createLinearRGBTransferFunction.ts +22 -0
- package/src/utilities/createSigmoidRGBTransferFunction.ts +63 -0
- package/src/utilities/createUInt16SharedArray.ts +43 -0
- package/src/utilities/createUint8SharedArray.ts +45 -0
- package/src/utilities/deepFreeze.ts +19 -0
- package/src/utilities/deepMerge.ts +81 -0
- package/src/utilities/getClosestImageId.ts +80 -0
- package/src/utilities/getClosestStackImageIndexForPoint.ts +116 -0
- package/src/utilities/getImageSliceDataForVolumeViewport.ts +61 -0
- package/src/utilities/getMinMax.ts +31 -0
- package/src/utilities/getRuntimeId.ts +54 -0
- package/src/utilities/getScalarDataType.ts +31 -0
- package/src/utilities/getScalingParameters.ts +35 -0
- package/src/utilities/getSliceRange.ts +86 -0
- package/src/utilities/getSpacingInNormalDirection.ts +44 -0
- package/src/utilities/getTargetVolumeAndSpacingInNormalDir.ts +126 -0
- package/src/utilities/getViewportImageCornersInWorld.ts +102 -0
- package/src/utilities/getViewportsWithImageURI.ts +46 -0
- package/src/utilities/getViewportsWithVolumeId.ts +38 -0
- package/src/utilities/getVoiFromSigmoidRGBTransferFunction.ts +23 -0
- package/src/utilities/getVolumeActorCorners.ts +24 -0
- package/src/utilities/getVolumeSliceRangeInfo.ts +52 -0
- package/src/utilities/getVolumeViewportScrollInfo.ts +32 -0
- package/src/utilities/getVolumeViewportsContainingSameVolumes.ts +58 -0
- package/src/utilities/hasNaNValues.ts +12 -0
- package/src/utilities/imageIdToURI.ts +10 -0
- package/src/utilities/imageToWorldCoords.ts +54 -0
- package/src/utilities/index.ts +100 -0
- package/src/utilities/indexWithinDimensions.ts +27 -0
- package/src/utilities/invertRgbTransferFunction.ts +36 -0
- package/src/utilities/isEqual.ts +27 -0
- package/src/utilities/isOpposite.ts +23 -0
- package/src/utilities/isTypedArray.ts +20 -0
- package/src/utilities/loadImageToCanvas.ts +80 -0
- package/src/utilities/planar.ts +91 -0
- package/src/utilities/renderToCanvas.ts +32 -0
- package/src/utilities/scaleRgbTransferFunction.ts +37 -0
- package/src/utilities/snapFocalPointToSlice.ts +78 -0
- package/src/utilities/spatialRegistrationMetadataProvider.ts +50 -0
- package/src/utilities/transformWorldToIndex.ts +16 -0
- package/src/utilities/triggerEvent.ts +38 -0
- package/src/utilities/uuidv4.ts +13 -0
- package/src/utilities/windowLevel.ts +39 -0
- package/src/utilities/worldToImageCoords.ts +64 -0
|
@@ -0,0 +1,1244 @@
|
|
|
1
|
+
import type { vtkCamera } from '@kitware/vtk.js/Rendering/Core/Camera';
|
|
2
|
+
import vtkMatrixBuilder from '@kitware/vtk.js/Common/Core/MatrixBuilder';
|
|
3
|
+
import vtkMath from '@kitware/vtk.js/Common/Core/Math';
|
|
4
|
+
import vtkPlane from '@kitware/vtk.js/Common/DataModel/Plane';
|
|
5
|
+
|
|
6
|
+
import { vec2, vec3 } from 'gl-matrix';
|
|
7
|
+
import _cloneDeep from 'lodash.clonedeep';
|
|
8
|
+
|
|
9
|
+
import Events from '../enums/Events';
|
|
10
|
+
import ViewportType from '../enums/ViewportType';
|
|
11
|
+
import renderingEngineCache from './renderingEngineCache';
|
|
12
|
+
import { triggerEvent, planar, isImageActor, actorIsA } from '../utilities';
|
|
13
|
+
import hasNaNValues from '../utilities/hasNaNValues';
|
|
14
|
+
import { RENDERING_DEFAULTS } from '../constants';
|
|
15
|
+
import type {
|
|
16
|
+
ICamera,
|
|
17
|
+
ActorEntry,
|
|
18
|
+
IRenderingEngine,
|
|
19
|
+
ViewportInputOptions,
|
|
20
|
+
Point2,
|
|
21
|
+
Point3,
|
|
22
|
+
FlipDirection,
|
|
23
|
+
EventTypes,
|
|
24
|
+
} from '../types';
|
|
25
|
+
import type { ViewportInput, IViewport } from '../types/IViewport';
|
|
26
|
+
import type { vtkSlabCamera } from './vtkClasses/vtkSlabCamera';
|
|
27
|
+
import { getConfiguration } from '../init';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* An object representing a single viewport, which is a camera
|
|
31
|
+
* looking into a viewport, and an associated target output `HTMLDivElement`.
|
|
32
|
+
* Viewport is a base class that can be extended to create a specific
|
|
33
|
+
* viewport type. Both VolumeViewport and StackViewport are subclasses
|
|
34
|
+
* of Viewport. Common logic for all viewports is contained in Viewport class
|
|
35
|
+
* which is camera properties/methods, vtk.js actors, and other common
|
|
36
|
+
* logic.
|
|
37
|
+
*/
|
|
38
|
+
class Viewport implements IViewport {
|
|
39
|
+
/** unique identifier for the viewport */
|
|
40
|
+
readonly id: string;
|
|
41
|
+
/** HTML element in DOM that is used for rendering the viewport */
|
|
42
|
+
readonly element: HTMLDivElement;
|
|
43
|
+
/** an internal canvas that is created on the provided HTML element */
|
|
44
|
+
readonly canvas: HTMLCanvasElement;
|
|
45
|
+
/** RenderingEngine id that the viewport belongs to */
|
|
46
|
+
readonly renderingEngineId: string;
|
|
47
|
+
/** Type of viewport */
|
|
48
|
+
readonly type: ViewportType;
|
|
49
|
+
protected flipHorizontal = false;
|
|
50
|
+
protected flipVertical = false;
|
|
51
|
+
public isDisabled: boolean;
|
|
52
|
+
|
|
53
|
+
/** sx of viewport on the offscreen canvas */
|
|
54
|
+
sx: number;
|
|
55
|
+
/** sy of viewport on the offscreen canvas */
|
|
56
|
+
sy: number;
|
|
57
|
+
/** sWidth of viewport on the offscreen canvas */
|
|
58
|
+
sWidth: number;
|
|
59
|
+
/** sHeight of viewport on the offscreen canvas */
|
|
60
|
+
sHeight: number;
|
|
61
|
+
/** a Map containing the actor uid and actors */
|
|
62
|
+
_actors: Map<string, any>;
|
|
63
|
+
/** Default options for the viewport which includes orientation, viewPlaneNormal and backgroundColor */
|
|
64
|
+
readonly defaultOptions: any;
|
|
65
|
+
/** options for the viewport which includes orientation axis and backgroundColor */
|
|
66
|
+
options: ViewportInputOptions;
|
|
67
|
+
private _suppressCameraModifiedEvents = false;
|
|
68
|
+
/** A flag representing if viewport methods should fire events or not */
|
|
69
|
+
readonly suppressEvents: boolean;
|
|
70
|
+
protected hasPixelSpacing = true;
|
|
71
|
+
/** The camera that is initially defined on the reset for
|
|
72
|
+
* the relative pan/zoom
|
|
73
|
+
*/
|
|
74
|
+
protected initialCamera: ICamera;
|
|
75
|
+
|
|
76
|
+
constructor(props: ViewportInput) {
|
|
77
|
+
this.id = props.id;
|
|
78
|
+
this.renderingEngineId = props.renderingEngineId;
|
|
79
|
+
this.type = props.type;
|
|
80
|
+
this.element = props.element;
|
|
81
|
+
this.canvas = props.canvas;
|
|
82
|
+
this.sx = props.sx;
|
|
83
|
+
this.sy = props.sy;
|
|
84
|
+
this.sWidth = props.sWidth;
|
|
85
|
+
this.sHeight = props.sHeight;
|
|
86
|
+
this._actors = new Map();
|
|
87
|
+
// Set data attributes for render events
|
|
88
|
+
this.element.setAttribute('data-viewport-uid', this.id);
|
|
89
|
+
this.element.setAttribute(
|
|
90
|
+
'data-rendering-engine-uid',
|
|
91
|
+
this.renderingEngineId
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
this.defaultOptions = _cloneDeep(props.defaultOptions);
|
|
95
|
+
this.suppressEvents = props.defaultOptions.suppressEvents
|
|
96
|
+
? props.defaultOptions.suppressEvents
|
|
97
|
+
: false;
|
|
98
|
+
this.options = _cloneDeep(props.defaultOptions);
|
|
99
|
+
this.isDisabled = false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getRotation: () => number;
|
|
103
|
+
getFrameOfReferenceUID: () => string;
|
|
104
|
+
canvasToWorld: (canvasPos: Point2) => Point3;
|
|
105
|
+
worldToCanvas: (worldPos: Point3) => Point2;
|
|
106
|
+
customRenderViewportToCanvas: () => unknown;
|
|
107
|
+
resize: () => void;
|
|
108
|
+
getProperties: () => void;
|
|
109
|
+
|
|
110
|
+
static get useCustomRenderingPipeline(): boolean {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Returns the rendering engine driving the `Viewport`.
|
|
116
|
+
*
|
|
117
|
+
* @returns The RenderingEngine instance.
|
|
118
|
+
*/
|
|
119
|
+
public getRenderingEngine(): IRenderingEngine {
|
|
120
|
+
return renderingEngineCache.get(this.renderingEngineId);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Returns the `vtkRenderer` responsible for rendering the `Viewport`.
|
|
125
|
+
*
|
|
126
|
+
* @returns The `vtkRenderer` for the `Viewport`.
|
|
127
|
+
*/
|
|
128
|
+
public getRenderer(): any {
|
|
129
|
+
const renderingEngine = this.getRenderingEngine();
|
|
130
|
+
|
|
131
|
+
if (!renderingEngine || renderingEngine.hasBeenDestroyed) {
|
|
132
|
+
throw new Error('Rendering engine has been destroyed');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return renderingEngine.offscreenMultiRenderWindow.getRenderer(this.id);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Renders the `Viewport` using the `RenderingEngine`.
|
|
140
|
+
*/
|
|
141
|
+
public render(): void {
|
|
142
|
+
const renderingEngine = this.getRenderingEngine();
|
|
143
|
+
|
|
144
|
+
renderingEngine.renderViewport(this.id);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Sets new options and (TODO) applies them.
|
|
149
|
+
*
|
|
150
|
+
* @param options - The viewport options to set.
|
|
151
|
+
* @param immediate - If `true`, renders the viewport after the options are set.
|
|
152
|
+
*/
|
|
153
|
+
public setOptions(options: ViewportInputOptions, immediate = false): void {
|
|
154
|
+
this.options = <ViewportInputOptions>_cloneDeep(options);
|
|
155
|
+
|
|
156
|
+
// TODO When this is needed we need to move the camera position.
|
|
157
|
+
// We can steal some logic from the tools we build to do this.
|
|
158
|
+
|
|
159
|
+
if (immediate) {
|
|
160
|
+
this.render();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Resets the options the `Viewport`'s `defaultOptions`
|
|
166
|
+
*
|
|
167
|
+
* @param immediate - If `true`, renders the viewport after the options are reset.
|
|
168
|
+
*/
|
|
169
|
+
public reset(immediate = false) {
|
|
170
|
+
this.options = _cloneDeep(this.defaultOptions);
|
|
171
|
+
|
|
172
|
+
// TODO When this is needed we need to move the camera position.
|
|
173
|
+
// We can steal some logic from the tools we build to do this.
|
|
174
|
+
|
|
175
|
+
if (immediate) {
|
|
176
|
+
this.render();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Flip the viewport on horizontal or vertical axis, this method
|
|
182
|
+
* works with vtk-js backed rendering pipeline.
|
|
183
|
+
*
|
|
184
|
+
* @param flipOptions - Flip options specifying the axis of flip
|
|
185
|
+
* @param flipOptions.flipHorizontal - Flip the viewport on horizontal axis
|
|
186
|
+
* @param flipOptions.flipVertical - Flip the viewport on vertical axis
|
|
187
|
+
*/
|
|
188
|
+
protected flip({ flipHorizontal, flipVertical }: FlipDirection): void {
|
|
189
|
+
const imageData = this.getDefaultImageData();
|
|
190
|
+
|
|
191
|
+
if (!imageData) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const camera = this.getCamera();
|
|
196
|
+
const { viewPlaneNormal, viewUp, focalPoint, position } = camera;
|
|
197
|
+
|
|
198
|
+
const viewRight = vec3.cross(vec3.create(), viewPlaneNormal, viewUp);
|
|
199
|
+
let viewUpToSet = vec3.copy(vec3.create(), viewUp);
|
|
200
|
+
const viewPlaneNormalToSet = vec3.negate(vec3.create(), viewPlaneNormal);
|
|
201
|
+
|
|
202
|
+
// for both flip horizontal and vertical we need to move the camera to the
|
|
203
|
+
// other side of the image
|
|
204
|
+
const distance = vec3.distance(position, focalPoint);
|
|
205
|
+
|
|
206
|
+
// If the pan has been applied, we need to be able
|
|
207
|
+
// apply the pan back
|
|
208
|
+
const dimensions = imageData.getDimensions();
|
|
209
|
+
const middleIJK = dimensions.map((d) => Math.floor(d / 2));
|
|
210
|
+
|
|
211
|
+
const idx = [middleIJK[0], middleIJK[1], middleIJK[2]];
|
|
212
|
+
const centeredFocalPoint = imageData.indexToWorld(idx, vec3.create());
|
|
213
|
+
|
|
214
|
+
const resetFocalPoint = this._getFocalPointForResetCamera(
|
|
215
|
+
centeredFocalPoint as Point3,
|
|
216
|
+
camera,
|
|
217
|
+
{ resetPan: true, resetToCenter: false }
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const panDir = vec3.subtract(vec3.create(), focalPoint, resetFocalPoint);
|
|
221
|
+
const panValue = vec3.length(panDir);
|
|
222
|
+
|
|
223
|
+
const getPanDir = (mirrorVec) => {
|
|
224
|
+
const panDirMirror = vec3.scale(
|
|
225
|
+
vec3.create(),
|
|
226
|
+
mirrorVec,
|
|
227
|
+
2 * vec3.dot(panDir, mirrorVec)
|
|
228
|
+
);
|
|
229
|
+
vec3.subtract(panDirMirror, panDirMirror, panDir);
|
|
230
|
+
vec3.normalize(panDirMirror, panDirMirror);
|
|
231
|
+
|
|
232
|
+
return panDirMirror;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Flipping horizontal mean that the camera should move
|
|
236
|
+
// to the other side of the image but looking at the
|
|
237
|
+
// same direction and same focal point
|
|
238
|
+
if (flipHorizontal) {
|
|
239
|
+
// we need to apply the pan value to the new focal point but in the direction
|
|
240
|
+
// that is mirrored on the viewUp for the flip horizontal and
|
|
241
|
+
// viewRight for the flip vertical
|
|
242
|
+
|
|
243
|
+
// mirror the pan direction based on the viewUp
|
|
244
|
+
const panDirMirror = getPanDir(viewUpToSet);
|
|
245
|
+
|
|
246
|
+
// move focal point from the resetFocalPoint to the newFocalPoint
|
|
247
|
+
// based on the panDirMirror and panValue
|
|
248
|
+
const newFocalPoint = vec3.scaleAndAdd(
|
|
249
|
+
vec3.create(),
|
|
250
|
+
resetFocalPoint,
|
|
251
|
+
panDirMirror,
|
|
252
|
+
panValue
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// move the camera position also the same way as the focal point
|
|
256
|
+
const newPosition = vec3.scaleAndAdd(
|
|
257
|
+
vec3.create(),
|
|
258
|
+
newFocalPoint,
|
|
259
|
+
viewPlaneNormalToSet,
|
|
260
|
+
distance
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
this.setCamera({
|
|
264
|
+
viewPlaneNormal: viewPlaneNormalToSet as Point3,
|
|
265
|
+
position: newPosition as Point3,
|
|
266
|
+
focalPoint: newFocalPoint as Point3,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
this.flipHorizontal = !this.flipHorizontal;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Flipping vertical mean that the camera should negate the view up
|
|
273
|
+
// and also move to the other side of the image but looking at the
|
|
274
|
+
if (flipVertical) {
|
|
275
|
+
viewUpToSet = vec3.negate(viewUpToSet, viewUp);
|
|
276
|
+
|
|
277
|
+
// we need to apply the pan value to the new focal point but in the direction
|
|
278
|
+
const panDirMirror = getPanDir(viewRight);
|
|
279
|
+
|
|
280
|
+
const newFocalPoint = vec3.scaleAndAdd(
|
|
281
|
+
vec3.create(),
|
|
282
|
+
resetFocalPoint,
|
|
283
|
+
panDirMirror,
|
|
284
|
+
panValue
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const newPosition = vec3.scaleAndAdd(
|
|
288
|
+
vec3.create(),
|
|
289
|
+
newFocalPoint,
|
|
290
|
+
viewPlaneNormalToSet,
|
|
291
|
+
distance
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
this.setCamera({
|
|
295
|
+
focalPoint: newFocalPoint as Point3,
|
|
296
|
+
viewPlaneNormal: viewPlaneNormalToSet as Point3,
|
|
297
|
+
viewUp: viewUpToSet as Point3,
|
|
298
|
+
position: newPosition as Point3,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
this.flipVertical = !this.flipVertical;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
this.render();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private getDefaultImageData(): any {
|
|
308
|
+
const actorEntry = this.getDefaultActor();
|
|
309
|
+
|
|
310
|
+
if (actorEntry && isImageActor(actorEntry)) {
|
|
311
|
+
return actorEntry.actor.getMapper().getInputData();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get the default actor
|
|
317
|
+
* @returns An actor entry.
|
|
318
|
+
*/
|
|
319
|
+
public getDefaultActor(): ActorEntry {
|
|
320
|
+
return this.getActors()[0];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get all the actors in the viewport
|
|
325
|
+
* @returns An array of ActorEntry objects.
|
|
326
|
+
*/
|
|
327
|
+
public getActors(): Array<ActorEntry> {
|
|
328
|
+
return Array.from(this._actors.values());
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get an actor by its UID
|
|
333
|
+
* @param actorUID - The unique ID of the actor.
|
|
334
|
+
* @returns An ActorEntry object.
|
|
335
|
+
*/
|
|
336
|
+
public getActor(actorUID: string): ActorEntry {
|
|
337
|
+
return this._actors.get(actorUID);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Get an actor UID by its index
|
|
342
|
+
* @param index - array index.
|
|
343
|
+
* @returns actorUID
|
|
344
|
+
*/
|
|
345
|
+
public getActorUIDByIndex(index: number): string {
|
|
346
|
+
const actor = this.getActors()[index];
|
|
347
|
+
if (actor) {
|
|
348
|
+
return actor.uid;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Get an actor by its index
|
|
354
|
+
* @param index - array index.
|
|
355
|
+
* @returns actorUID
|
|
356
|
+
*/
|
|
357
|
+
public getActorByIndex(index: number): ActorEntry {
|
|
358
|
+
return this.getActors()[index];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* It removes all actors from the viewport and then adds the actors from the array.
|
|
363
|
+
* @param actors - An array of ActorEntry objects.
|
|
364
|
+
*/
|
|
365
|
+
public setActors(actors: Array<ActorEntry>): void {
|
|
366
|
+
this.removeAllActors();
|
|
367
|
+
const resetCameraPanAndZoom = true;
|
|
368
|
+
// when we set the actor we need to reset the camera to initialize the
|
|
369
|
+
// camera focal point with the bounds of the actors.
|
|
370
|
+
this.addActors(actors, resetCameraPanAndZoom);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Remove the actor from the viewport
|
|
375
|
+
* @param actorUID - The unique identifier for the actor.
|
|
376
|
+
*/
|
|
377
|
+
_removeActor(actorUID: string): void {
|
|
378
|
+
const actorEntry = this.getActor(actorUID);
|
|
379
|
+
if (!actorEntry) {
|
|
380
|
+
console.warn(`Actor ${actorUID} does not exist for this viewport`);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const renderer = this.getRenderer();
|
|
384
|
+
renderer.removeViewProp(actorEntry.actor); // removeActor not implemented in vtk?
|
|
385
|
+
this._actors.delete(actorUID);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Remove the actors with the given UIDs from the viewport
|
|
390
|
+
* @param actorUIDs - An array of actor UIDs to remove.
|
|
391
|
+
*/
|
|
392
|
+
public removeActors(actorUIDs: Array<string>): void {
|
|
393
|
+
actorUIDs.forEach((actorUID) => {
|
|
394
|
+
this._removeActor(actorUID);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Add a list of actors (actor entries) to the viewport
|
|
400
|
+
* @param resetCameraPanAndZoom - force reset pan and zoom of the camera,
|
|
401
|
+
* default value is false.
|
|
402
|
+
* @param actors - An array of ActorEntry objects.
|
|
403
|
+
*/
|
|
404
|
+
public addActors(
|
|
405
|
+
actors: Array<ActorEntry>,
|
|
406
|
+
resetCameraPanAndZoom = false
|
|
407
|
+
): void {
|
|
408
|
+
const renderingEngine = this.getRenderingEngine();
|
|
409
|
+
if (!renderingEngine || renderingEngine.hasBeenDestroyed) {
|
|
410
|
+
console.warn(
|
|
411
|
+
'Viewport::addActors::Rendering engine has not been initialized or has been destroyed'
|
|
412
|
+
);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
actors.forEach((actor) => this.addActor(actor));
|
|
417
|
+
|
|
418
|
+
// set the clipping planes for the actors
|
|
419
|
+
this.resetCamera(resetCameraPanAndZoom, resetCameraPanAndZoom);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Add an actor to the viewport including its id, its actor and slabThickness
|
|
424
|
+
* if defined
|
|
425
|
+
* @param actorEntry - ActorEntry
|
|
426
|
+
* @param actorEntry.uid - The unique identifier for the actor.
|
|
427
|
+
* @param actorEntry.actor - The volume actor.
|
|
428
|
+
* @param actorEntry.slabThickness - The slab thickness.
|
|
429
|
+
*/
|
|
430
|
+
public addActor(actorEntry: ActorEntry): void {
|
|
431
|
+
const { uid: actorUID, actor } = actorEntry;
|
|
432
|
+
const renderingEngine = this.getRenderingEngine();
|
|
433
|
+
|
|
434
|
+
if (!renderingEngine || renderingEngine.hasBeenDestroyed) {
|
|
435
|
+
console.warn(
|
|
436
|
+
`Cannot add actor UID of ${actorUID} Rendering Engine has been destroyed`
|
|
437
|
+
);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (!actorUID || !actor) {
|
|
442
|
+
throw new Error('Actors should have uid and vtk Actor properties');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (this.getActor(actorUID)) {
|
|
446
|
+
console.warn(`Actor ${actorUID} already exists for this viewport`);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const renderer = this.getRenderer();
|
|
451
|
+
renderer.addActor(actor);
|
|
452
|
+
this._actors.set(actorUID, Object.assign({}, actorEntry));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Remove all actors from the renderer
|
|
457
|
+
*/
|
|
458
|
+
public removeAllActors(): void {
|
|
459
|
+
this.getRenderer().removeAllViewProps();
|
|
460
|
+
this._actors = new Map();
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Reset the camera to the default viewport camera without firing events
|
|
466
|
+
*/
|
|
467
|
+
protected resetCameraNoEvent(): void {
|
|
468
|
+
this._suppressCameraModifiedEvents = true;
|
|
469
|
+
this.resetCamera();
|
|
470
|
+
this._suppressCameraModifiedEvents = false;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Sets the camera to the default viewport camera without firing events
|
|
475
|
+
* @param camera - The camera to use for the viewport.
|
|
476
|
+
*/
|
|
477
|
+
protected setCameraNoEvent(camera: ICamera): void {
|
|
478
|
+
this._suppressCameraModifiedEvents = true;
|
|
479
|
+
this.setCamera(camera);
|
|
480
|
+
this._suppressCameraModifiedEvents = false;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Calculates the intersections between the volume's boundaries and the viewplane.
|
|
485
|
+
* 1) Determine the viewplane using the camera's ViewplaneNormal and focalPoint.
|
|
486
|
+
* 2) Using volumeBounds, calculate the line equation for the 3D volume's 12 edges.
|
|
487
|
+
* 3) Intersect each edge to the viewPlane and see whether the intersection point is inside the volume bounds.
|
|
488
|
+
* 4) Return list of intersection points
|
|
489
|
+
* It should be noted that intersection points may range from 3 to 6 points.
|
|
490
|
+
* Orthogonal views have four points of intersection.
|
|
491
|
+
*
|
|
492
|
+
* @param imageData - vtkImageData
|
|
493
|
+
* @param focalPoint - camera focal point
|
|
494
|
+
* @param normal - view plane normal
|
|
495
|
+
* @returns intersections list
|
|
496
|
+
*/
|
|
497
|
+
private _getViewImageDataIntersections(imageData, focalPoint, normal) {
|
|
498
|
+
// Viewplane equation: Ax+By+Cz=D
|
|
499
|
+
const A = normal[0];
|
|
500
|
+
const B = normal[1];
|
|
501
|
+
const C = normal[2];
|
|
502
|
+
const D = A * focalPoint[0] + B * focalPoint[1] + C * focalPoint[2];
|
|
503
|
+
|
|
504
|
+
// Computing the edges of the 3D cube
|
|
505
|
+
const bounds = imageData.getBounds();
|
|
506
|
+
const edges = this._getEdges(bounds);
|
|
507
|
+
|
|
508
|
+
const intersections = [];
|
|
509
|
+
|
|
510
|
+
for (const edge of edges) {
|
|
511
|
+
// start point: [x0, y0, z0], end point: [x1, y1, z1]
|
|
512
|
+
const [[x0, y0, z0], [x1, y1, z1]] = edge;
|
|
513
|
+
// Check if the edge is parallel to plane
|
|
514
|
+
if (A * (x1 - x0) + B * (y1 - y0) + C * (z1 - z0) === 0) {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const intersectionPoint = planar.linePlaneIntersection(
|
|
518
|
+
[x0, y0, z0],
|
|
519
|
+
[x1, y1, z1],
|
|
520
|
+
[A, B, C, D]
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
if (this._isInBounds(intersectionPoint, bounds)) {
|
|
524
|
+
intersections.push(intersectionPoint);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return intersections;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Resets the camera based on the rendering volume(s) bounds. If
|
|
533
|
+
* resetPan and resetZoom are true it places the focal point at the center of
|
|
534
|
+
* the volume (or slice); otherwise, only the camera zoom and camera Pan or Zoom
|
|
535
|
+
* is reset for the current view.
|
|
536
|
+
* @param resetPan - If true, the camera focal point is reset to the center of the volume (slice)
|
|
537
|
+
* @param resetZoom - If true, the camera zoom is reset to the default zoom
|
|
538
|
+
* @param storeAsInitialCamera - If true, reset camera is stored as the initial camera (to allow differences to
|
|
539
|
+
* be detected for pan/zoom values)
|
|
540
|
+
* @returns boolean
|
|
541
|
+
*/
|
|
542
|
+
protected resetCamera(
|
|
543
|
+
resetPan = true,
|
|
544
|
+
resetZoom = true,
|
|
545
|
+
resetToCenter = true,
|
|
546
|
+
storeAsInitialCamera = true
|
|
547
|
+
): boolean {
|
|
548
|
+
const renderer = this.getRenderer();
|
|
549
|
+
|
|
550
|
+
// fix the flip right away, since we rely on the viewPlaneNormal and
|
|
551
|
+
// viewUp for later. Basically, we need to flip back if flipHorizontal
|
|
552
|
+
// is true or flipVertical is true
|
|
553
|
+
this.setCamera({
|
|
554
|
+
flipHorizontal: false,
|
|
555
|
+
flipVertical: false,
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
const previousCamera = _cloneDeep(this.getCamera());
|
|
559
|
+
|
|
560
|
+
const bounds = renderer.computeVisiblePropBounds();
|
|
561
|
+
const focalPoint = <Point3>[0, 0, 0];
|
|
562
|
+
const imageData = this.getDefaultImageData();
|
|
563
|
+
|
|
564
|
+
// Todo: remove this, this is just for tests passing
|
|
565
|
+
if (imageData) {
|
|
566
|
+
const spc = imageData.getSpacing();
|
|
567
|
+
|
|
568
|
+
bounds[0] = bounds[0] + spc[0] / 2;
|
|
569
|
+
bounds[1] = bounds[1] - spc[0] / 2;
|
|
570
|
+
bounds[2] = bounds[2] + spc[1] / 2;
|
|
571
|
+
bounds[3] = bounds[3] - spc[1] / 2;
|
|
572
|
+
bounds[4] = bounds[4] + spc[2] / 2;
|
|
573
|
+
bounds[5] = bounds[5] - spc[2] / 2;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const activeCamera = this.getVtkActiveCamera();
|
|
577
|
+
const viewPlaneNormal = <Point3>activeCamera.getViewPlaneNormal();
|
|
578
|
+
const viewUp = <Point3>activeCamera.getViewUp();
|
|
579
|
+
|
|
580
|
+
// Reset the perspective zoom factors, otherwise subsequent zooms will cause
|
|
581
|
+
// the view angle to become very small and cause bad depth sorting.
|
|
582
|
+
// todo: parallel projection only
|
|
583
|
+
|
|
584
|
+
focalPoint[0] = (bounds[0] + bounds[1]) / 2.0;
|
|
585
|
+
focalPoint[1] = (bounds[2] + bounds[3]) / 2.0;
|
|
586
|
+
focalPoint[2] = (bounds[4] + bounds[5]) / 2.0;
|
|
587
|
+
|
|
588
|
+
if (imageData) {
|
|
589
|
+
const dimensions = imageData.getDimensions();
|
|
590
|
+
const middleIJK = dimensions.map((d) => Math.floor(d / 2));
|
|
591
|
+
|
|
592
|
+
const idx = [middleIJK[0], middleIJK[1], middleIJK[2]];
|
|
593
|
+
imageData.indexToWorld(idx, focalPoint);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const { widthWorld, heightWorld } =
|
|
597
|
+
this._getWorldDistanceViewUpAndViewRight(bounds, viewUp, viewPlaneNormal);
|
|
598
|
+
|
|
599
|
+
const canvasSize = [this.sWidth, this.sHeight];
|
|
600
|
+
|
|
601
|
+
const boundsAspectRatio = widthWorld / heightWorld;
|
|
602
|
+
const canvasAspectRatio = canvasSize[0] / canvasSize[1];
|
|
603
|
+
|
|
604
|
+
let radius;
|
|
605
|
+
|
|
606
|
+
if (boundsAspectRatio < canvasAspectRatio) {
|
|
607
|
+
// can fit full height, so use it.
|
|
608
|
+
radius = heightWorld / 2;
|
|
609
|
+
} else {
|
|
610
|
+
const scaleFactor = boundsAspectRatio / canvasAspectRatio;
|
|
611
|
+
|
|
612
|
+
radius = (heightWorld * scaleFactor) / 2;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
//const angle = vtkMath.radiansFromDegrees(activeCamera.getViewAngle())
|
|
616
|
+
const parallelScale = 1.1 * radius;
|
|
617
|
+
|
|
618
|
+
let w1 = bounds[1] - bounds[0];
|
|
619
|
+
let w2 = bounds[3] - bounds[2];
|
|
620
|
+
let w3 = bounds[5] - bounds[4];
|
|
621
|
+
w1 *= w1;
|
|
622
|
+
w2 *= w2;
|
|
623
|
+
w3 *= w3;
|
|
624
|
+
radius = w1 + w2 + w3;
|
|
625
|
+
|
|
626
|
+
// If we have just a single point, pick a radius of 1.0
|
|
627
|
+
radius = radius === 0 ? 1.0 : radius;
|
|
628
|
+
|
|
629
|
+
// compute the radius of the enclosing sphere
|
|
630
|
+
radius = Math.sqrt(radius) * 0.5;
|
|
631
|
+
|
|
632
|
+
const distance = 1.1 * radius;
|
|
633
|
+
|
|
634
|
+
const viewUpToSet: Point3 =
|
|
635
|
+
Math.abs(vtkMath.dot(viewUp, viewPlaneNormal)) > 0.999
|
|
636
|
+
? [-viewUp[2], viewUp[0], viewUp[1]]
|
|
637
|
+
: viewUp;
|
|
638
|
+
|
|
639
|
+
const focalPointToSet = this._getFocalPointForResetCamera(
|
|
640
|
+
focalPoint,
|
|
641
|
+
previousCamera,
|
|
642
|
+
{ resetPan, resetToCenter }
|
|
643
|
+
);
|
|
644
|
+
|
|
645
|
+
const positionToSet: Point3 = [
|
|
646
|
+
focalPointToSet[0] + distance * viewPlaneNormal[0],
|
|
647
|
+
focalPointToSet[1] + distance * viewPlaneNormal[1],
|
|
648
|
+
focalPointToSet[2] + distance * viewPlaneNormal[2],
|
|
649
|
+
];
|
|
650
|
+
|
|
651
|
+
renderer.resetCameraClippingRange(bounds);
|
|
652
|
+
|
|
653
|
+
const clippingRangeToUse: Point2 = [
|
|
654
|
+
-RENDERING_DEFAULTS.MAXIMUM_RAY_DISTANCE,
|
|
655
|
+
RENDERING_DEFAULTS.MAXIMUM_RAY_DISTANCE,
|
|
656
|
+
];
|
|
657
|
+
|
|
658
|
+
activeCamera.setPhysicalScale(radius);
|
|
659
|
+
activeCamera.setPhysicalTranslation(
|
|
660
|
+
-focalPointToSet[0],
|
|
661
|
+
-focalPointToSet[1],
|
|
662
|
+
-focalPointToSet[2]
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
this.setCamera({
|
|
666
|
+
parallelScale: resetZoom ? parallelScale : previousCamera.parallelScale,
|
|
667
|
+
focalPoint: focalPointToSet,
|
|
668
|
+
position: positionToSet,
|
|
669
|
+
viewAngle: 90,
|
|
670
|
+
viewUp: viewUpToSet,
|
|
671
|
+
clippingRange: clippingRangeToUse,
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const modifiedCamera = _cloneDeep(this.getCamera());
|
|
675
|
+
|
|
676
|
+
if (storeAsInitialCamera) {
|
|
677
|
+
this.setInitialCamera(modifiedCamera);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const RESET_CAMERA_EVENT = {
|
|
681
|
+
type: 'ResetCameraEvent',
|
|
682
|
+
renderer,
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// Here to let parallel/distributed compositing intercept
|
|
686
|
+
// and do the right thing.
|
|
687
|
+
renderer.invokeEvent(RESET_CAMERA_EVENT);
|
|
688
|
+
|
|
689
|
+
this.triggerCameraModifiedEventIfNecessary(previousCamera, modifiedCamera);
|
|
690
|
+
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Sets the provided camera as the initial camera.
|
|
696
|
+
* This allows computing differences applied later as compared to the initial
|
|
697
|
+
* position, for things like zoom and pan.
|
|
698
|
+
* @param camera - to store as the initial value.
|
|
699
|
+
*/
|
|
700
|
+
protected setInitialCamera(camera: ICamera): void {
|
|
701
|
+
this.initialCamera = camera;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Helper function to return the current canvas pan value.
|
|
706
|
+
*
|
|
707
|
+
* @returns a Point2 containing the current pan values
|
|
708
|
+
* on the canvas,
|
|
709
|
+
* computed from the current camera, where the initial pan
|
|
710
|
+
* value is [0,0].
|
|
711
|
+
*/
|
|
712
|
+
public getPan(): Point2 {
|
|
713
|
+
const activeCamera = this.getVtkActiveCamera();
|
|
714
|
+
const focalPoint = activeCamera.getFocalPoint() as Point3;
|
|
715
|
+
|
|
716
|
+
const zero3 = this.canvasToWorld([0, 0]);
|
|
717
|
+
const initialCanvasFocal = this.worldToCanvas(
|
|
718
|
+
<Point3>vec3.subtract(vec3.create(), this.initialCamera.focalPoint, zero3)
|
|
719
|
+
);
|
|
720
|
+
const currentCanvasFocal = this.worldToCanvas(
|
|
721
|
+
<Point3>vec3.subtract(vec3.create(), focalPoint, zero3)
|
|
722
|
+
);
|
|
723
|
+
const result = <Point2>(
|
|
724
|
+
vec2.subtract(vec2.create(), initialCanvasFocal, currentCanvasFocal)
|
|
725
|
+
);
|
|
726
|
+
return result;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Sets the canvas pan value relative to the initial view position of 0,0
|
|
731
|
+
* Modifies the camera to perform the pan.
|
|
732
|
+
*/
|
|
733
|
+
public setPan(pan: Point2, storeAsInitialCamera = false): void {
|
|
734
|
+
const previousCamera = this.getCamera();
|
|
735
|
+
const { focalPoint, position } = previousCamera;
|
|
736
|
+
const zero3 = this.canvasToWorld([0, 0]);
|
|
737
|
+
const delta2 = vec2.subtract(vec2.create(), pan, this.getPan());
|
|
738
|
+
if (
|
|
739
|
+
Math.abs(delta2[0]) < 1 &&
|
|
740
|
+
Math.abs(delta2[1]) < 1 &&
|
|
741
|
+
!storeAsInitialCamera
|
|
742
|
+
) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const delta = vec3.subtract(
|
|
746
|
+
vec3.create(),
|
|
747
|
+
this.canvasToWorld(<Point2>delta2),
|
|
748
|
+
zero3
|
|
749
|
+
);
|
|
750
|
+
const newFocal = vec3.subtract(vec3.create(), focalPoint, delta);
|
|
751
|
+
const newPosition = vec3.subtract(vec3.create(), position, delta);
|
|
752
|
+
this.setCamera(
|
|
753
|
+
{
|
|
754
|
+
...previousCamera,
|
|
755
|
+
focalPoint: newFocal as Point3,
|
|
756
|
+
position: newPosition as Point3,
|
|
757
|
+
},
|
|
758
|
+
storeAsInitialCamera
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Returns a current zoom level relative to the initial parallel scale
|
|
764
|
+
* originally applied to the image. That is, on initial display,
|
|
765
|
+
* the zoom level is 1. Computed as a function of the camera.
|
|
766
|
+
*/
|
|
767
|
+
public getZoom(): number {
|
|
768
|
+
const activeCamera = this.getVtkActiveCamera();
|
|
769
|
+
const { parallelScale: initialParallelScale } = this.initialCamera;
|
|
770
|
+
return initialParallelScale / activeCamera.getParallelScale();
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/** Zooms the image using parallel scale by updating the camera value.
|
|
774
|
+
* @param value - The relative parallel scale to apply. It is relative
|
|
775
|
+
* to the initial offsets value.
|
|
776
|
+
* @param storeAsInitialCamera - can be set to true to reset the camera
|
|
777
|
+
* after applying this zoom as the initial camera. A subsequent getZoom
|
|
778
|
+
* call will return "1", but the zoom will have been applied.
|
|
779
|
+
*/
|
|
780
|
+
public setZoom(value: number, storeAsInitialCamera = false): void {
|
|
781
|
+
const camera = this.getCamera();
|
|
782
|
+
const { parallelScale: initialParallelScale } = this.initialCamera;
|
|
783
|
+
const parallelScale = initialParallelScale / value;
|
|
784
|
+
if (camera.parallelScale === parallelScale && !storeAsInitialCamera) {
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
this.setCamera(
|
|
788
|
+
{
|
|
789
|
+
...camera,
|
|
790
|
+
parallelScale,
|
|
791
|
+
},
|
|
792
|
+
storeAsInitialCamera
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Because the focalPoint is always in the centre of the viewport,
|
|
798
|
+
* we must do planar computations if the frame (image "slice") is to be preserved.
|
|
799
|
+
* 1. Calculate the intersection of the view plane with the imageData
|
|
800
|
+
* which results in points of intersection (minimum of 3, maximum of 6)
|
|
801
|
+
* 2. Calculate average of the intersection points to get newFocalPoint
|
|
802
|
+
* 3. Set the new focalPoint
|
|
803
|
+
* @param imageData - imageData
|
|
804
|
+
* @returns focalPoint
|
|
805
|
+
*/
|
|
806
|
+
private _getFocalPointForViewPlaneReset(imageData) {
|
|
807
|
+
// Todo: move some where else
|
|
808
|
+
const { focalPoint, viewPlaneNormal: normal } = this.getCamera();
|
|
809
|
+
const intersections = this._getViewImageDataIntersections(
|
|
810
|
+
imageData,
|
|
811
|
+
focalPoint,
|
|
812
|
+
normal
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
let x = 0;
|
|
816
|
+
let y = 0;
|
|
817
|
+
let z = 0;
|
|
818
|
+
|
|
819
|
+
intersections.forEach(([point_x, point_y, point_z]) => {
|
|
820
|
+
x += point_x;
|
|
821
|
+
y += point_y;
|
|
822
|
+
z += point_z;
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
const newFocalPoint = <Point3>[
|
|
826
|
+
x / intersections.length,
|
|
827
|
+
y / intersections.length,
|
|
828
|
+
z / intersections.length,
|
|
829
|
+
];
|
|
830
|
+
// Set the focal point on the average of the intersection points
|
|
831
|
+
return newFocalPoint;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Gets the target output canvas for the `Viewport`.
|
|
836
|
+
*
|
|
837
|
+
* @returns an HTMLCanvasElement.
|
|
838
|
+
*/
|
|
839
|
+
public getCanvas(): HTMLCanvasElement {
|
|
840
|
+
return <HTMLCanvasElement>this.canvas;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Gets the active vtkCamera for the viewport.
|
|
844
|
+
*
|
|
845
|
+
* @returns vtk driven camera
|
|
846
|
+
*/
|
|
847
|
+
protected getVtkActiveCamera(): vtkCamera | vtkSlabCamera {
|
|
848
|
+
const renderer = this.getRenderer();
|
|
849
|
+
|
|
850
|
+
return renderer.getActiveCamera();
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Get the camera's current state
|
|
855
|
+
* @returns The camera object.
|
|
856
|
+
*/
|
|
857
|
+
public getCamera(): ICamera {
|
|
858
|
+
const vtkCamera = this.getVtkActiveCamera();
|
|
859
|
+
|
|
860
|
+
return {
|
|
861
|
+
viewUp: <Point3>vtkCamera.getViewUp(),
|
|
862
|
+
viewPlaneNormal: <Point3>vtkCamera.getViewPlaneNormal(),
|
|
863
|
+
position: <Point3>vtkCamera.getPosition(),
|
|
864
|
+
focalPoint: <Point3>vtkCamera.getFocalPoint(),
|
|
865
|
+
parallelProjection: vtkCamera.getParallelProjection(),
|
|
866
|
+
parallelScale: vtkCamera.getParallelScale(),
|
|
867
|
+
viewAngle: vtkCamera.getViewAngle(),
|
|
868
|
+
flipHorizontal: this.flipHorizontal,
|
|
869
|
+
flipVertical: this.flipVertical,
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Set the camera parameters
|
|
875
|
+
* @param cameraInterface - ICamera
|
|
876
|
+
* @param storeAsInitialCamera - to set the provided camera as the initial one,
|
|
877
|
+
* used to compute differences for things like pan and zoom.
|
|
878
|
+
*/
|
|
879
|
+
public setCamera(
|
|
880
|
+
cameraInterface: ICamera,
|
|
881
|
+
storeAsInitialCamera = false
|
|
882
|
+
): void {
|
|
883
|
+
const vtkCamera = this.getVtkActiveCamera();
|
|
884
|
+
const previousCamera = _cloneDeep(this.getCamera());
|
|
885
|
+
const updatedCamera = Object.assign({}, previousCamera, cameraInterface);
|
|
886
|
+
const {
|
|
887
|
+
viewUp,
|
|
888
|
+
viewPlaneNormal,
|
|
889
|
+
position,
|
|
890
|
+
focalPoint,
|
|
891
|
+
parallelScale,
|
|
892
|
+
viewAngle,
|
|
893
|
+
flipHorizontal,
|
|
894
|
+
flipVertical,
|
|
895
|
+
clippingRange,
|
|
896
|
+
} = cameraInterface;
|
|
897
|
+
|
|
898
|
+
// Note: Flip camera should be two separate calls since
|
|
899
|
+
// for flip, we need to flip the viewportNormal, and if
|
|
900
|
+
// flipHorizontal, and flipVertical are both true, that would
|
|
901
|
+
// the logic would be incorrect. So instead, we handle flip Horizontal
|
|
902
|
+
// and flipVertical separately.
|
|
903
|
+
if (flipHorizontal !== undefined) {
|
|
904
|
+
// flip if not flipped but requested to flip OR if flipped but requested to
|
|
905
|
+
// not flip
|
|
906
|
+
const flipH =
|
|
907
|
+
(flipHorizontal && !this.flipHorizontal) ||
|
|
908
|
+
(!flipHorizontal && this.flipHorizontal);
|
|
909
|
+
|
|
910
|
+
if (flipH) {
|
|
911
|
+
this.flip({ flipHorizontal: flipH });
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (flipVertical !== undefined) {
|
|
916
|
+
const flipV =
|
|
917
|
+
(flipVertical && !this.flipVertical) ||
|
|
918
|
+
(!flipVertical && this.flipVertical);
|
|
919
|
+
|
|
920
|
+
if (flipV) {
|
|
921
|
+
this.flip({ flipVertical: flipV });
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (viewUp !== undefined) {
|
|
926
|
+
vtkCamera.setViewUp(viewUp);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if (viewPlaneNormal !== undefined) {
|
|
930
|
+
vtkCamera.setDirectionOfProjection(
|
|
931
|
+
-viewPlaneNormal[0],
|
|
932
|
+
-viewPlaneNormal[1],
|
|
933
|
+
-viewPlaneNormal[2]
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (position !== undefined) {
|
|
938
|
+
vtkCamera.setPosition(...position);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (focalPoint !== undefined) {
|
|
942
|
+
vtkCamera.setFocalPoint(...focalPoint);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if (parallelScale !== undefined) {
|
|
946
|
+
vtkCamera.setParallelScale(parallelScale);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (viewAngle !== undefined) {
|
|
950
|
+
vtkCamera.setViewAngle(viewAngle);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
if (clippingRange !== undefined) {
|
|
954
|
+
vtkCamera.setClippingRange(clippingRange);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// update clippingPlanes if volume viewports
|
|
958
|
+
const actorEntry = this.getDefaultActor();
|
|
959
|
+
|
|
960
|
+
if (!actorEntry || !actorEntry.actor) {
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
const isImageSlice = actorIsA(actorEntry, 'vtkImageSlice');
|
|
965
|
+
|
|
966
|
+
if (!isImageSlice) {
|
|
967
|
+
this.updateClippingPlanesForActors(updatedCamera);
|
|
968
|
+
} else {
|
|
969
|
+
const renderer = this.getRenderer();
|
|
970
|
+
renderer.resetCameraClippingRange();
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
if (storeAsInitialCamera) {
|
|
974
|
+
this.setInitialCamera(updatedCamera);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
this.triggerCameraModifiedEventIfNecessary(
|
|
978
|
+
previousCamera,
|
|
979
|
+
this.getCamera()
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Trigger camera modified event
|
|
985
|
+
* @param cameraInterface - ICamera
|
|
986
|
+
* @param cameraInterface - ICamera
|
|
987
|
+
*/
|
|
988
|
+
public triggerCameraModifiedEventIfNecessary(
|
|
989
|
+
previousCamera: ICamera,
|
|
990
|
+
updatedCamera: ICamera
|
|
991
|
+
): void {
|
|
992
|
+
if (!this._suppressCameraModifiedEvents && !this.suppressEvents) {
|
|
993
|
+
const eventDetail: EventTypes.CameraModifiedEventDetail = {
|
|
994
|
+
previousCamera,
|
|
995
|
+
camera: updatedCamera,
|
|
996
|
+
element: this.element,
|
|
997
|
+
viewportId: this.id,
|
|
998
|
+
renderingEngineId: this.renderingEngineId,
|
|
999
|
+
rotation: this.getRotation(),
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
triggerEvent(this.element, Events.CAMERA_MODIFIED, eventDetail);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Updates the actors clipping planes orientation from the camera properties
|
|
1008
|
+
* @param updatedCamera - ICamera
|
|
1009
|
+
*/
|
|
1010
|
+
protected updateClippingPlanesForActors(updatedCamera: ICamera): void {
|
|
1011
|
+
const actorEntries = this.getActors();
|
|
1012
|
+
actorEntries.forEach((actorEntry) => {
|
|
1013
|
+
// we assume that the first two clipping plane of the mapper are always
|
|
1014
|
+
// the 'camera' clipping. Update clipping planes only if the actor is
|
|
1015
|
+
// a vtkVolume
|
|
1016
|
+
if (!actorEntry.actor) {
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
const mapper = actorEntry.actor.getMapper();
|
|
1021
|
+
const vtkPlanes = mapper.getClippingPlanes();
|
|
1022
|
+
|
|
1023
|
+
let slabThickness = RENDERING_DEFAULTS.MINIMUM_SLAB_THICKNESS;
|
|
1024
|
+
if (actorEntry.slabThickness) {
|
|
1025
|
+
slabThickness = actorEntry.slabThickness;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
const { viewPlaneNormal, focalPoint } = updatedCamera;
|
|
1029
|
+
|
|
1030
|
+
this.setOrientationOfClippingPlanes(
|
|
1031
|
+
vtkPlanes,
|
|
1032
|
+
slabThickness,
|
|
1033
|
+
viewPlaneNormal,
|
|
1034
|
+
focalPoint
|
|
1035
|
+
);
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
public setOrientationOfClippingPlanes(
|
|
1040
|
+
vtkPlanes: Array<vtkPlane>,
|
|
1041
|
+
slabThickness: number,
|
|
1042
|
+
viewPlaneNormal: Point3,
|
|
1043
|
+
focalPoint: Point3
|
|
1044
|
+
): void {
|
|
1045
|
+
if (vtkPlanes.length < 2) {
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const scaledDistance = <Point3>[
|
|
1050
|
+
viewPlaneNormal[0],
|
|
1051
|
+
viewPlaneNormal[1],
|
|
1052
|
+
viewPlaneNormal[2],
|
|
1053
|
+
];
|
|
1054
|
+
vtkMath.multiplyScalar(scaledDistance, slabThickness);
|
|
1055
|
+
|
|
1056
|
+
vtkPlanes[0].setNormal(viewPlaneNormal);
|
|
1057
|
+
const newOrigin1 = <Point3>[0, 0, 0];
|
|
1058
|
+
vtkMath.subtract(focalPoint, scaledDistance, newOrigin1);
|
|
1059
|
+
vtkPlanes[0].setOrigin(newOrigin1);
|
|
1060
|
+
|
|
1061
|
+
vtkPlanes[1].setNormal(
|
|
1062
|
+
-viewPlaneNormal[0],
|
|
1063
|
+
-viewPlaneNormal[1],
|
|
1064
|
+
-viewPlaneNormal[2]
|
|
1065
|
+
);
|
|
1066
|
+
const newOrigin2 = <Point3>[0, 0, 0];
|
|
1067
|
+
vtkMath.add(focalPoint, scaledDistance, newOrigin2);
|
|
1068
|
+
vtkPlanes[1].setOrigin(newOrigin2);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
private _getWorldDistanceViewUpAndViewRight(bounds, viewUp, viewPlaneNormal) {
|
|
1072
|
+
const viewUpCorners = this._getCorners(bounds);
|
|
1073
|
+
const viewRightCorners = this._getCorners(bounds);
|
|
1074
|
+
|
|
1075
|
+
const viewRight = vec3.cross(vec3.create(), viewUp, viewPlaneNormal);
|
|
1076
|
+
|
|
1077
|
+
let transform = vtkMatrixBuilder
|
|
1078
|
+
.buildFromDegree()
|
|
1079
|
+
.identity()
|
|
1080
|
+
.rotateFromDirections(viewUp, [1, 0, 0]);
|
|
1081
|
+
|
|
1082
|
+
viewUpCorners.forEach((pt) => transform.apply(pt));
|
|
1083
|
+
|
|
1084
|
+
// range is now maximum X distance
|
|
1085
|
+
let minY = Infinity;
|
|
1086
|
+
let maxY = -Infinity;
|
|
1087
|
+
for (let i = 0; i < 8; i++) {
|
|
1088
|
+
const y = viewUpCorners[i][0];
|
|
1089
|
+
if (y > maxY) {
|
|
1090
|
+
maxY = y;
|
|
1091
|
+
}
|
|
1092
|
+
if (y < minY) {
|
|
1093
|
+
minY = y;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
transform = vtkMatrixBuilder
|
|
1098
|
+
.buildFromDegree()
|
|
1099
|
+
.identity()
|
|
1100
|
+
.rotateFromDirections(
|
|
1101
|
+
[viewRight[0], viewRight[1], viewRight[2]],
|
|
1102
|
+
[1, 0, 0]
|
|
1103
|
+
);
|
|
1104
|
+
|
|
1105
|
+
viewRightCorners.forEach((pt) => transform.apply(pt));
|
|
1106
|
+
|
|
1107
|
+
// range is now maximum Y distance
|
|
1108
|
+
let minX = Infinity;
|
|
1109
|
+
let maxX = -Infinity;
|
|
1110
|
+
for (let i = 0; i < 8; i++) {
|
|
1111
|
+
const x = viewRightCorners[i][0];
|
|
1112
|
+
if (x > maxX) {
|
|
1113
|
+
maxX = x;
|
|
1114
|
+
}
|
|
1115
|
+
if (x < minX) {
|
|
1116
|
+
minX = x;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
return { widthWorld: maxX - minX, heightWorld: maxY - minY };
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
protected _shouldUse16BitTexture() {
|
|
1124
|
+
const { useNorm16Texture, preferSizeOverAccuracy } =
|
|
1125
|
+
getConfiguration().rendering;
|
|
1126
|
+
return useNorm16Texture || preferSizeOverAccuracy;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
_getCorners(bounds: Array<number>): Array<number>[] {
|
|
1130
|
+
return [
|
|
1131
|
+
[bounds[0], bounds[2], bounds[4]],
|
|
1132
|
+
[bounds[0], bounds[2], bounds[5]],
|
|
1133
|
+
[bounds[0], bounds[3], bounds[4]],
|
|
1134
|
+
[bounds[0], bounds[3], bounds[5]],
|
|
1135
|
+
[bounds[1], bounds[2], bounds[4]],
|
|
1136
|
+
[bounds[1], bounds[2], bounds[5]],
|
|
1137
|
+
[bounds[1], bounds[3], bounds[4]],
|
|
1138
|
+
[bounds[1], bounds[3], bounds[5]],
|
|
1139
|
+
];
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
_getFocalPointForResetCamera(
|
|
1143
|
+
centeredFocalPoint: Point3,
|
|
1144
|
+
previousCamera: ICamera,
|
|
1145
|
+
{ resetPan = true, resetToCenter = true }
|
|
1146
|
+
): Point3 {
|
|
1147
|
+
if (resetToCenter && resetPan) {
|
|
1148
|
+
return centeredFocalPoint;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
if (resetToCenter && !resetPan) {
|
|
1152
|
+
return hasNaNValues(previousCamera.focalPoint)
|
|
1153
|
+
? centeredFocalPoint
|
|
1154
|
+
: previousCamera.focalPoint;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
if (!resetToCenter && resetPan) {
|
|
1158
|
+
// this is an interesting case that means the reset camera should not
|
|
1159
|
+
// change the slice (default behavior is to go to the center of the
|
|
1160
|
+
// image), and rather just reset the pan on the slice that is currently
|
|
1161
|
+
// being viewed
|
|
1162
|
+
const oldCamera = previousCamera;
|
|
1163
|
+
const oldFocalPoint = oldCamera.focalPoint;
|
|
1164
|
+
const oldViewPlaneNormal = oldCamera.viewPlaneNormal;
|
|
1165
|
+
|
|
1166
|
+
const vectorFromOldFocalPointToCenteredFocalPoint = vec3.subtract(
|
|
1167
|
+
vec3.create(),
|
|
1168
|
+
centeredFocalPoint,
|
|
1169
|
+
oldFocalPoint
|
|
1170
|
+
);
|
|
1171
|
+
|
|
1172
|
+
const distanceFromOldFocalPointToCenteredFocalPoint = vec3.dot(
|
|
1173
|
+
vectorFromOldFocalPointToCenteredFocalPoint,
|
|
1174
|
+
oldViewPlaneNormal
|
|
1175
|
+
);
|
|
1176
|
+
|
|
1177
|
+
const newFocalPoint = vec3.scaleAndAdd(
|
|
1178
|
+
vec3.create(),
|
|
1179
|
+
centeredFocalPoint,
|
|
1180
|
+
oldViewPlaneNormal,
|
|
1181
|
+
-1 * distanceFromOldFocalPointToCenteredFocalPoint
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
return [newFocalPoint[0], newFocalPoint[1], newFocalPoint[2]];
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
if (!resetPan && !resetToCenter) {
|
|
1188
|
+
// this means the reset camera should not change the slice and should not
|
|
1189
|
+
// touch the pan either.
|
|
1190
|
+
return hasNaNValues(previousCamera.focalPoint)
|
|
1191
|
+
? centeredFocalPoint
|
|
1192
|
+
: previousCamera.focalPoint;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Determines whether or not the 3D point position is inside the boundaries of the 3D imageData.
|
|
1198
|
+
* @param point - 3D coordinate
|
|
1199
|
+
* @param bounds - Bounds of the image
|
|
1200
|
+
* @returns boolean
|
|
1201
|
+
*/
|
|
1202
|
+
_isInBounds(point: Point3, bounds: number[]): boolean {
|
|
1203
|
+
const [xMin, xMax, yMin, yMax, zMin, zMax] = bounds;
|
|
1204
|
+
const [x, y, z] = point;
|
|
1205
|
+
if (x < xMin || x > xMax || y < yMin || y > yMax || z < zMin || z > zMax) {
|
|
1206
|
+
return false;
|
|
1207
|
+
}
|
|
1208
|
+
return true;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Returns a list of edges for the imageData bounds, which are
|
|
1213
|
+
* the cube edges in the case of volumeViewport edges.
|
|
1214
|
+
* p1: front, bottom, left
|
|
1215
|
+
* p2: front, top, left
|
|
1216
|
+
* p3: back, bottom, left
|
|
1217
|
+
* p4: back, top, left
|
|
1218
|
+
* p5: front, bottom, right
|
|
1219
|
+
* p6: front, top, right
|
|
1220
|
+
* p7: back, bottom, right
|
|
1221
|
+
* p8: back, top, right
|
|
1222
|
+
* @param bounds - Bounds of the renderer
|
|
1223
|
+
* @returns Edges of the containing bounds
|
|
1224
|
+
*/
|
|
1225
|
+
_getEdges(bounds: Array<number>): Array<[number[], number[]]> {
|
|
1226
|
+
const [p1, p2, p3, p4, p5, p6, p7, p8] = this._getCorners(bounds);
|
|
1227
|
+
return [
|
|
1228
|
+
[p1, p2],
|
|
1229
|
+
[p1, p5],
|
|
1230
|
+
[p1, p3],
|
|
1231
|
+
[p2, p4],
|
|
1232
|
+
[p2, p6],
|
|
1233
|
+
[p3, p4],
|
|
1234
|
+
[p3, p7],
|
|
1235
|
+
[p4, p8],
|
|
1236
|
+
[p5, p7],
|
|
1237
|
+
[p5, p6],
|
|
1238
|
+
[p6, p8],
|
|
1239
|
+
[p7, p8],
|
|
1240
|
+
];
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
export default Viewport;
|