@cornerstonejs/core 1.42.0 → 1.43.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.
- package/dist/cjs/RenderingEngine/BaseVolumeViewport.js +3 -0
- package/dist/cjs/RenderingEngine/BaseVolumeViewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/StackViewport.js +2 -1
- package/dist/cjs/RenderingEngine/StackViewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/Viewport.d.ts +1 -0
- package/dist/cjs/RenderingEngine/Viewport.js +3 -0
- package/dist/cjs/RenderingEngine/Viewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/helpers/createVolumeActor.js +2 -1
- package/dist/cjs/RenderingEngine/helpers/createVolumeActor.js.map +1 -1
- package/dist/cjs/RenderingEngine/vtkClasses/vtkStreamingOpenGLVolumeMapper.js +1 -1
- package/dist/cjs/RenderingEngine/vtkClasses/vtkStreamingOpenGLVolumeMapper.js.map +1 -1
- package/dist/cjs/cache/cache.d.ts +6 -3
- package/dist/cjs/cache/cache.js +43 -6
- package/dist/cjs/cache/cache.js.map +1 -1
- package/dist/cjs/cache/classes/ImageVolume.d.ts +25 -5
- package/dist/cjs/cache/classes/ImageVolume.js +302 -13
- package/dist/cjs/cache/classes/ImageVolume.js.map +1 -1
- package/dist/cjs/eventTarget.d.ts +1 -0
- package/dist/cjs/eventTarget.js +13 -1
- package/dist/cjs/eventTarget.js.map +1 -1
- package/dist/cjs/init.js +2 -0
- package/dist/cjs/init.js.map +1 -1
- package/dist/cjs/loaders/imageLoader.d.ts +6 -1
- package/dist/cjs/loaders/imageLoader.js +26 -12
- package/dist/cjs/loaders/imageLoader.js.map +1 -1
- package/dist/cjs/loaders/volumeLoader.d.ts +13 -9
- package/dist/cjs/loaders/volumeLoader.js +58 -5
- package/dist/cjs/loaders/volumeLoader.js.map +1 -1
- package/dist/cjs/types/BoundsLPS.d.ts +3 -0
- package/dist/cjs/types/BoundsLPS.js +3 -0
- package/dist/cjs/types/BoundsLPS.js.map +1 -0
- package/dist/cjs/types/Cornerstone3DConfig.d.ts +1 -0
- package/dist/cjs/types/IDynamicImageVolume.d.ts +2 -2
- package/dist/cjs/types/IImage.d.ts +5 -0
- package/dist/cjs/types/IImageVolume.d.ts +7 -2
- package/dist/cjs/types/ILoadObject.d.ts +2 -2
- package/dist/cjs/types/IViewport.d.ts +1 -0
- package/dist/cjs/types/IVolume.d.ts +3 -26
- package/dist/cjs/types/ImageVolumeProps.d.ts +6 -0
- package/dist/cjs/types/ImageVolumeProps.js +3 -0
- package/dist/cjs/types/ImageVolumeProps.js.map +1 -0
- package/dist/cjs/types/VolumeProps.d.ts +27 -0
- package/dist/cjs/types/VolumeProps.js +3 -0
- package/dist/cjs/types/VolumeProps.js.map +1 -0
- package/dist/cjs/types/index.d.ts +5 -2
- package/dist/cjs/utilities/VoxelManager.d.ts +2 -2
- package/dist/cjs/utilities/VoxelManager.js +13 -4
- package/dist/cjs/utilities/VoxelManager.js.map +1 -1
- package/dist/cjs/utilities/cacheUtils.d.ts +2 -0
- package/dist/cjs/utilities/cacheUtils.js +96 -0
- package/dist/cjs/utilities/cacheUtils.js.map +1 -0
- package/dist/cjs/utilities/convertStackToVolumeViewport.d.ts +12 -0
- package/dist/cjs/utilities/convertStackToVolumeViewport.js +61 -0
- package/dist/cjs/utilities/convertStackToVolumeViewport.js.map +1 -0
- package/dist/cjs/utilities/convertVolumeToStackViewport.d.ts +9 -0
- package/dist/cjs/utilities/convertVolumeToStackViewport.js +95 -0
- package/dist/cjs/utilities/convertVolumeToStackViewport.js.map +1 -0
- package/dist/cjs/utilities/generateVolumePropsFromImageIds.d.ts +3 -0
- package/dist/cjs/utilities/generateVolumePropsFromImageIds.js +124 -0
- package/dist/cjs/utilities/generateVolumePropsFromImageIds.js.map +1 -0
- package/dist/cjs/utilities/index.d.ts +6 -1
- package/dist/cjs/utilities/index.js +12 -1
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/planar.d.ts +1 -1
- package/dist/cjs/utilities/planar.js +5 -1
- package/dist/cjs/utilities/planar.js.map +1 -1
- package/dist/cjs/utilities/roundNumber.d.ts +4 -0
- package/dist/cjs/utilities/roundNumber.js +36 -0
- package/dist/cjs/utilities/roundNumber.js.map +1 -0
- package/dist/esm/RenderingEngine/BaseVolumeViewport.js +3 -0
- package/dist/esm/RenderingEngine/BaseVolumeViewport.js.map +1 -1
- package/dist/esm/RenderingEngine/StackViewport.js +2 -1
- package/dist/esm/RenderingEngine/StackViewport.js.map +1 -1
- package/dist/esm/RenderingEngine/Viewport.js +3 -0
- package/dist/esm/RenderingEngine/Viewport.js.map +1 -1
- package/dist/esm/RenderingEngine/helpers/createVolumeActor.js +1 -1
- package/dist/esm/RenderingEngine/helpers/createVolumeActor.js.map +1 -1
- package/dist/esm/RenderingEngine/vtkClasses/vtkStreamingOpenGLVolumeMapper.js +1 -1
- package/dist/esm/RenderingEngine/vtkClasses/vtkStreamingOpenGLVolumeMapper.js.map +1 -1
- package/dist/esm/cache/cache.js +43 -6
- package/dist/esm/cache/cache.js.map +1 -1
- package/dist/esm/cache/classes/ImageVolume.js +279 -14
- package/dist/esm/cache/classes/ImageVolume.js.map +1 -1
- package/dist/esm/eventTarget.js +13 -1
- package/dist/esm/eventTarget.js.map +1 -1
- package/dist/esm/init.js +2 -0
- package/dist/esm/init.js.map +1 -1
- package/dist/esm/loaders/imageLoader.js +23 -9
- package/dist/esm/loaders/imageLoader.js.map +1 -1
- package/dist/esm/loaders/volumeLoader.js +58 -5
- package/dist/esm/loaders/volumeLoader.js.map +1 -1
- package/dist/esm/types/BoundsLPS.js +2 -0
- package/dist/esm/types/BoundsLPS.js.map +1 -0
- package/dist/esm/types/ImageVolumeProps.js +2 -0
- package/dist/esm/types/ImageVolumeProps.js.map +1 -0
- package/dist/esm/types/VolumeProps.js +2 -0
- package/dist/esm/types/VolumeProps.js.map +1 -0
- package/dist/esm/utilities/VoxelManager.js +13 -4
- package/dist/esm/utilities/VoxelManager.js.map +1 -1
- package/dist/esm/utilities/cacheUtils.js +65 -0
- package/dist/esm/utilities/cacheUtils.js.map +1 -0
- package/dist/esm/utilities/convertStackToVolumeViewport.js +49 -0
- package/dist/esm/utilities/convertStackToVolumeViewport.js.map +1 -0
- package/dist/esm/utilities/convertVolumeToStackViewport.js +58 -0
- package/dist/esm/utilities/convertVolumeToStackViewport.js.map +1 -0
- package/dist/esm/utilities/generateVolumePropsFromImageIds.js +118 -0
- package/dist/esm/utilities/generateVolumePropsFromImageIds.js.map +1 -0
- package/dist/esm/utilities/index.js +6 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/planar.js +5 -1
- package/dist/esm/utilities/planar.js.map +1 -1
- package/dist/esm/utilities/roundNumber.js +33 -0
- package/dist/esm/utilities/roundNumber.js.map +1 -0
- package/dist/types/RenderingEngine/BaseVolumeViewport.d.ts.map +1 -1
- package/dist/types/RenderingEngine/StackViewport.d.ts.map +1 -1
- package/dist/types/RenderingEngine/Viewport.d.ts +1 -0
- package/dist/types/RenderingEngine/Viewport.d.ts.map +1 -1
- package/dist/types/RenderingEngine/vtkClasses/vtkStreamingOpenGLVolumeMapper.d.ts.map +1 -1
- package/dist/types/cache/cache.d.ts +6 -3
- package/dist/types/cache/cache.d.ts.map +1 -1
- package/dist/types/cache/classes/ImageVolume.d.ts +25 -5
- package/dist/types/cache/classes/ImageVolume.d.ts.map +1 -1
- package/dist/types/eventTarget.d.ts +1 -0
- package/dist/types/eventTarget.d.ts.map +1 -1
- package/dist/types/loaders/imageLoader.d.ts +6 -1
- package/dist/types/loaders/imageLoader.d.ts.map +1 -1
- package/dist/types/loaders/volumeLoader.d.ts +13 -9
- package/dist/types/loaders/volumeLoader.d.ts.map +1 -1
- package/dist/types/types/BoundsLPS.d.ts +4 -0
- package/dist/types/types/BoundsLPS.d.ts.map +1 -0
- package/dist/types/types/Cornerstone3DConfig.d.ts +1 -0
- package/dist/types/types/Cornerstone3DConfig.d.ts.map +1 -1
- package/dist/types/types/IDynamicImageVolume.d.ts +2 -2
- package/dist/types/types/IDynamicImageVolume.d.ts.map +1 -1
- package/dist/types/types/IImage.d.ts +5 -0
- package/dist/types/types/IImage.d.ts.map +1 -1
- package/dist/types/types/IImageVolume.d.ts +7 -2
- package/dist/types/types/IImageVolume.d.ts.map +1 -1
- package/dist/types/types/ILoadObject.d.ts +2 -2
- package/dist/types/types/ILoadObject.d.ts.map +1 -1
- package/dist/types/types/IViewport.d.ts +1 -0
- package/dist/types/types/IViewport.d.ts.map +1 -1
- package/dist/types/types/IVolume.d.ts +3 -26
- package/dist/types/types/IVolume.d.ts.map +1 -1
- package/dist/types/types/ImageVolumeProps.d.ts +7 -0
- package/dist/types/types/ImageVolumeProps.d.ts.map +1 -0
- package/dist/types/types/VolumeProps.d.ts +28 -0
- package/dist/types/types/VolumeProps.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +5 -2
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/utilities/VoxelManager.d.ts +2 -2
- package/dist/types/utilities/VoxelManager.d.ts.map +1 -1
- package/dist/types/utilities/cacheUtils.d.ts +3 -0
- package/dist/types/utilities/cacheUtils.d.ts.map +1 -0
- package/dist/types/utilities/convertStackToVolumeViewport.d.ts +13 -0
- package/dist/types/utilities/convertStackToVolumeViewport.d.ts.map +1 -0
- package/dist/types/utilities/convertVolumeToStackViewport.d.ts +10 -0
- package/dist/types/utilities/convertVolumeToStackViewport.d.ts.map +1 -0
- package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts +4 -0
- package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts.map +1 -0
- package/dist/types/utilities/index.d.ts +6 -1
- package/dist/types/utilities/index.d.ts.map +1 -1
- package/dist/types/utilities/planar.d.ts +1 -1
- package/dist/types/utilities/planar.d.ts.map +1 -1
- package/dist/types/utilities/roundNumber.d.ts +5 -0
- package/dist/types/utilities/roundNumber.d.ts.map +1 -0
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/src/RenderingEngine/BaseVolumeViewport.ts +4 -1
- package/src/RenderingEngine/StackViewport.ts +3 -1
- package/src/RenderingEngine/Viewport.ts +9 -1
- package/src/RenderingEngine/helpers/createVolumeActor.ts +1 -1
- package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLVolumeMapper.js +3 -26
- package/src/cache/cache.ts +91 -7
- package/src/cache/classes/ImageVolume.ts +535 -21
- package/src/eventTarget.ts +19 -1
- package/src/init.ts +2 -2
- package/src/loaders/imageLoader.ts +58 -10
- package/src/loaders/volumeLoader.ts +139 -23
- package/src/types/BoundsLPS.ts +5 -0
- package/src/types/Cornerstone3DConfig.ts +12 -0
- package/src/types/IDynamicImageVolume.ts +2 -2
- package/src/types/IImage.ts +6 -0
- package/src/types/IImageVolume.ts +14 -2
- package/src/types/ILoadObject.ts +2 -2
- package/src/types/IViewport.ts +2 -1
- package/src/types/IVolume.ts +4 -41
- package/src/types/ImageVolumeProps.ts +15 -0
- package/src/types/VolumeProps.ts +57 -0
- package/src/types/index.ts +7 -2
- package/src/utilities/VoxelManager.ts +17 -6
- package/src/utilities/cacheUtils.ts +121 -0
- package/src/utilities/convertStackToVolumeViewport.ts +115 -0
- package/src/utilities/convertVolumeToStackViewport.ts +125 -0
- package/src/utilities/generateVolumePropsFromImageIds.ts +183 -0
- package/src/utilities/index.ts +11 -0
- package/src/utilities/planar.ts +12 -1
- package/src/utilities/roundNumber.ts +56 -0
package/src/types/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type Cornerstone3DConfig from './Cornerstone3DConfig';
|
|
|
3
3
|
import type ICamera from './ICamera';
|
|
4
4
|
import type IEnabledElement from './IEnabledElement';
|
|
5
5
|
import type ICache from './ICache';
|
|
6
|
-
import type { IVolume
|
|
6
|
+
import type { IVolume } from './IVolume';
|
|
7
7
|
import type { VOI, VOIRange } from './voi';
|
|
8
8
|
import type DisplayArea from './displayArea';
|
|
9
9
|
import type ImageLoaderFn from './ImageLoaderFn';
|
|
@@ -106,6 +106,9 @@ import type {
|
|
|
106
106
|
VideoViewportInput,
|
|
107
107
|
} from './VideoViewportTypes';
|
|
108
108
|
import type BoundsIJK from './BoundsIJK';
|
|
109
|
+
import type { ImageVolumeProps } from './ImageVolumeProps';
|
|
110
|
+
import type { VolumeProps } from './VolumeProps';
|
|
111
|
+
import type BoundsLPS from './BoundsLPS';
|
|
109
112
|
|
|
110
113
|
export type {
|
|
111
114
|
// config
|
|
@@ -118,9 +121,9 @@ export type {
|
|
|
118
121
|
IEnabledElement,
|
|
119
122
|
ICache,
|
|
120
123
|
IVolume,
|
|
121
|
-
VolumeScalarData,
|
|
122
124
|
IViewportId,
|
|
123
125
|
IImageVolume,
|
|
126
|
+
ImageVolumeProps,
|
|
124
127
|
IDynamicImageVolume,
|
|
125
128
|
IRenderingEngine,
|
|
126
129
|
ScalingParameters,
|
|
@@ -214,6 +217,8 @@ export type {
|
|
|
214
217
|
InternalVideoCamera,
|
|
215
218
|
VideoViewportInput,
|
|
216
219
|
BoundsIJK,
|
|
220
|
+
BoundsLPS,
|
|
217
221
|
Color,
|
|
218
222
|
ColorLUT,
|
|
223
|
+
VolumeProps,
|
|
219
224
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BoundsIJK, Point3,
|
|
1
|
+
import type { BoundsIJK, Point3, PixelDataTypedArray } from '../types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* This is a simple, standard interface to values associated with a voxel.
|
|
@@ -12,7 +12,7 @@ export default class VoxelManager<T> {
|
|
|
12
12
|
] as BoundsIJK;
|
|
13
13
|
|
|
14
14
|
// Provide direct access to the underlying data, if any
|
|
15
|
-
public scalarData:
|
|
15
|
+
public scalarData: PixelDataTypedArray;
|
|
16
16
|
public map: Map<number, T>;
|
|
17
17
|
public sourceVoxelManager: VoxelManager<T>;
|
|
18
18
|
public isInObject: (pointIPS, pointIJK) => boolean;
|
|
@@ -216,10 +216,21 @@ export default class VoxelManager<T> {
|
|
|
216
216
|
* Extends the bounds of this object to include the specified point
|
|
217
217
|
*/
|
|
218
218
|
public static addBounds(bounds: BoundsIJK, point: Point3) {
|
|
219
|
-
bounds
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
if (!bounds) {
|
|
220
|
+
bounds = [
|
|
221
|
+
[Infinity, -Infinity],
|
|
222
|
+
[Infinity, -Infinity],
|
|
223
|
+
[Infinity, -Infinity],
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Directly update the bounds for each axis
|
|
228
|
+
bounds[0][0] = Math.min(point[0], bounds[0][0]);
|
|
229
|
+
bounds[0][1] = Math.max(point[0], bounds[0][1]);
|
|
230
|
+
bounds[1][0] = Math.min(point[1], bounds[1][0]);
|
|
231
|
+
bounds[1][1] = Math.max(point[1], bounds[1][1]);
|
|
232
|
+
bounds[2][0] = Math.min(point[2], bounds[2][0]);
|
|
233
|
+
bounds[2][1] = Math.max(point[2], bounds[2][1]);
|
|
223
234
|
}
|
|
224
235
|
|
|
225
236
|
/**
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import cache, { ImageVolume } from '../cache';
|
|
2
|
+
import { Events } from '../enums';
|
|
3
|
+
import eventTarget from '../eventTarget';
|
|
4
|
+
import { getConfiguration, getShouldUseSharedArrayBuffer } from '../init';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This function will check if the cache optimization is enabled and if it is
|
|
8
|
+
* it will check if the created volume was derived from an already cached stack
|
|
9
|
+
* of images, if so it will go back to the image cache and create a view at the
|
|
10
|
+
* correct offset of the bigger volume array buffer, this will save memory.
|
|
11
|
+
*
|
|
12
|
+
* @param volumeId - The volumeId that will be checked for cache optimization
|
|
13
|
+
*/
|
|
14
|
+
export function setupCacheOptimizationEventListener(volumeId) {
|
|
15
|
+
const { enableCacheOptimization } = getConfiguration();
|
|
16
|
+
const shouldUseSAB = getShouldUseSharedArrayBuffer();
|
|
17
|
+
|
|
18
|
+
const performOptimization = enableCacheOptimization && shouldUseSAB;
|
|
19
|
+
if (!performOptimization) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
eventTarget.addEventListenerOnce(
|
|
24
|
+
Events.IMAGE_VOLUME_LOADING_COMPLETED,
|
|
25
|
+
(evt) => {
|
|
26
|
+
if (evt.detail.volumeId !== volumeId) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const volume = cache.getVolume(volumeId);
|
|
31
|
+
|
|
32
|
+
performCacheOptimizationForVolume(volume);
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Performs cache optimization for a volume by replacing the pixel data of each image
|
|
39
|
+
* in the image cache (if found) with a view of the volume's scalar data.
|
|
40
|
+
* @param options - The options for cache optimization.
|
|
41
|
+
* @param options.volumeId - The ID of the volume.
|
|
42
|
+
*/
|
|
43
|
+
export function performCacheOptimizationForVolume(volume) {
|
|
44
|
+
if (!(volume instanceof ImageVolume)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const scalarData = volume.getScalarData();
|
|
49
|
+
|
|
50
|
+
volume.imageCacheOffsetMap.size > 0
|
|
51
|
+
? _processImageCacheOffsetMap(volume, scalarData)
|
|
52
|
+
: _processVolumeImages(volume, scalarData);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* This function will process the volume images and replace the pixel data of each
|
|
57
|
+
* image in the image cache (if found) with a view of the volume's scalar data.
|
|
58
|
+
* This function is used when the volume is derived from an already cached stack
|
|
59
|
+
* of images.
|
|
60
|
+
*
|
|
61
|
+
* @param volume - The volume to process.
|
|
62
|
+
* @param scalarData - The scalar data to use for the volume.
|
|
63
|
+
*/
|
|
64
|
+
function _processImageCacheOffsetMap(volume, scalarData) {
|
|
65
|
+
volume.imageCacheOffsetMap.forEach(({ offset }, imageId) => {
|
|
66
|
+
const image = cache.getImage(imageId);
|
|
67
|
+
if (!image) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
_updateImageWithScalarDataView(image, scalarData, offset);
|
|
72
|
+
cache.decrementImageCacheSize(image.sizeInBytes);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* This function will process the volume images and replace the pixel data of each
|
|
78
|
+
* image in the image cache (if found) with a view of the volume's scalar data.
|
|
79
|
+
* This function is used when the volume is not derived from an already cached stack
|
|
80
|
+
* of images.
|
|
81
|
+
*
|
|
82
|
+
* @param volume - The volume to process.
|
|
83
|
+
* @param scalarData - The scalar data to use for the volume.
|
|
84
|
+
*/
|
|
85
|
+
function _processVolumeImages(volume, scalarData) {
|
|
86
|
+
volume.imageIds.forEach((imageId) => {
|
|
87
|
+
const image = cache.getImage(imageId);
|
|
88
|
+
if (!image) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const index = volume.getImageIdIndex(imageId);
|
|
93
|
+
const offset = index * image.getPixelData().byteLength;
|
|
94
|
+
|
|
95
|
+
_updateImageWithScalarDataView(image, scalarData, offset);
|
|
96
|
+
cache.decrementImageCacheSize(image.sizeInBytes);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function _updateImageWithScalarDataView(image, scalarData, offset) {
|
|
101
|
+
const pixelData = image.imageFrame
|
|
102
|
+
? image.imageFrame.pixelData
|
|
103
|
+
: image.getPixelData();
|
|
104
|
+
|
|
105
|
+
const view = new pixelData.constructor(
|
|
106
|
+
scalarData.buffer,
|
|
107
|
+
offset,
|
|
108
|
+
pixelData.length
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
image.getPixelData = () => view;
|
|
112
|
+
|
|
113
|
+
if (image.imageFrame) {
|
|
114
|
+
image.imageFrame.pixelData = view;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
image.bufferView = {
|
|
118
|
+
buffer: scalarData.buffer,
|
|
119
|
+
offset,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { IStackViewport, IVolumeViewport, Point3 } from '../types';
|
|
2
|
+
import { setVolumesForViewports } from '../RenderingEngine/helpers';
|
|
3
|
+
import {
|
|
4
|
+
createAndCacheVolume,
|
|
5
|
+
getUnknownVolumeLoaderSchema,
|
|
6
|
+
} from '../loaders/volumeLoader';
|
|
7
|
+
import { Events, OrientationAxis, ViewportType } from '../enums';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Converts a stack viewport to a volume viewport.
|
|
11
|
+
*
|
|
12
|
+
* @param params - The parameters for the conversion.
|
|
13
|
+
* @param params.viewport - The stack viewport to convert.
|
|
14
|
+
* @param params.options - The options for the conversion.
|
|
15
|
+
* @param [params.options.volumeId] - The volumeId that will get generated, it should have the volume loader schema defined if not we will use the default one.
|
|
16
|
+
* @param [params.options.viewportId] - The viewportId that will get used for new viewport. If not provided, the stack viewport id will be used.
|
|
17
|
+
* @param [params.options.background] - The background color of the volume viewport.
|
|
18
|
+
* @returns The converted volume viewport.
|
|
19
|
+
*/
|
|
20
|
+
async function convertStackToVolumeViewport({
|
|
21
|
+
viewport,
|
|
22
|
+
options,
|
|
23
|
+
}: {
|
|
24
|
+
viewport: IStackViewport;
|
|
25
|
+
options: {
|
|
26
|
+
volumeId: string;
|
|
27
|
+
viewportId?: string;
|
|
28
|
+
background?: Point3;
|
|
29
|
+
orientation?: OrientationAxis;
|
|
30
|
+
};
|
|
31
|
+
}): Promise<IVolumeViewport> {
|
|
32
|
+
const renderingEngine = viewport.getRenderingEngine();
|
|
33
|
+
|
|
34
|
+
let { volumeId } = options;
|
|
35
|
+
|
|
36
|
+
// if there is no loader schema for the volume Id, we will use the default one
|
|
37
|
+
// which we can get from the volume loader
|
|
38
|
+
if (volumeId.split(':').length === 1) {
|
|
39
|
+
const schema = getUnknownVolumeLoaderSchema();
|
|
40
|
+
volumeId = `${schema}:${volumeId}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { id, element } = viewport;
|
|
44
|
+
const viewportId = options.viewportId || id;
|
|
45
|
+
|
|
46
|
+
const imageIds = viewport.getImageIds();
|
|
47
|
+
|
|
48
|
+
// It is important to keep the camera before enabling the viewport
|
|
49
|
+
const prevCamera = viewport.getCamera();
|
|
50
|
+
|
|
51
|
+
// this will disable the stack viewport and remove it from the toolGroup
|
|
52
|
+
renderingEngine.enableElement({
|
|
53
|
+
viewportId,
|
|
54
|
+
type: ViewportType.ORTHOGRAPHIC,
|
|
55
|
+
element,
|
|
56
|
+
defaultOptions: {
|
|
57
|
+
background: options.background,
|
|
58
|
+
orientation: options.orientation,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Ideally here we should be able to just create a local volume and not use the
|
|
63
|
+
// volume louder but we don't know if the stack is already pre-cached for all its
|
|
64
|
+
// imageIds or not so we just let the loader handle it and we have cache
|
|
65
|
+
// optimizations in place to avoid fetching the same imageId if it is already
|
|
66
|
+
// cached
|
|
67
|
+
const volume = await createAndCacheVolume(volumeId, {
|
|
68
|
+
imageIds,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
volume.load();
|
|
72
|
+
|
|
73
|
+
// we should get the new viewport from the renderingEngine since the stack viewport
|
|
74
|
+
// was disabled and replaced with a volume viewport of the same id
|
|
75
|
+
const volumeViewport = <IVolumeViewport>(
|
|
76
|
+
renderingEngine.getViewport(viewportId)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
setVolumesForViewports(
|
|
80
|
+
renderingEngine,
|
|
81
|
+
[
|
|
82
|
+
{
|
|
83
|
+
volumeId,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
[viewportId]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const volumeViewportNewVolumeHandler = () => {
|
|
90
|
+
volumeViewport.setCamera({
|
|
91
|
+
...prevCamera,
|
|
92
|
+
});
|
|
93
|
+
volumeViewport.render();
|
|
94
|
+
|
|
95
|
+
element.removeEventListener(
|
|
96
|
+
Events.VOLUME_VIEWPORT_NEW_VOLUME,
|
|
97
|
+
volumeViewportNewVolumeHandler
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const addVolumeViewportNewVolumeListener = () => {
|
|
102
|
+
element.addEventListener(
|
|
103
|
+
Events.VOLUME_VIEWPORT_NEW_VOLUME,
|
|
104
|
+
volumeViewportNewVolumeHandler
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
addVolumeViewportNewVolumeListener();
|
|
109
|
+
|
|
110
|
+
volumeViewport.render();
|
|
111
|
+
|
|
112
|
+
return volumeViewport;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { convertStackToVolumeViewport };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as Types from '../types';
|
|
2
|
+
import cache, { ImageVolume } from '../cache';
|
|
3
|
+
import { ViewportType } from '../enums';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts a volume viewport to a stack viewport.
|
|
7
|
+
*
|
|
8
|
+
* @param params - The parameters for the conversion.
|
|
9
|
+
* @param params.viewport - The volume viewport to convert.
|
|
10
|
+
* @param params.options - The conversion options.
|
|
11
|
+
* @param [params.options.viewportId] - The new stackViewportId, If not provided, the volume viewport id will be used.
|
|
12
|
+
* @param [params.options.background] - The background color of the stack viewport.
|
|
13
|
+
* @param [params.options.decache] - Whether to decache the volume. Defaults to false.
|
|
14
|
+
*
|
|
15
|
+
* @returns The converted stack viewport.
|
|
16
|
+
*/
|
|
17
|
+
async function convertVolumeToStackViewport({
|
|
18
|
+
viewport,
|
|
19
|
+
options,
|
|
20
|
+
}: {
|
|
21
|
+
viewport: Types.IVolumeViewport;
|
|
22
|
+
options: {
|
|
23
|
+
viewportId?: string;
|
|
24
|
+
background?: Types.Point3;
|
|
25
|
+
};
|
|
26
|
+
}): Promise<Types.IStackViewport> {
|
|
27
|
+
const volumeViewport = viewport;
|
|
28
|
+
const { id, element } = volumeViewport;
|
|
29
|
+
const renderingEngine = viewport.getRenderingEngine();
|
|
30
|
+
const imageIdIndex = viewport.getCurrentImageIdIndex();
|
|
31
|
+
|
|
32
|
+
const { background } = options;
|
|
33
|
+
const viewportId = options.viewportId || id;
|
|
34
|
+
|
|
35
|
+
const actorEntry = volumeViewport.getDefaultActor();
|
|
36
|
+
const { uid: volumeId } = actorEntry;
|
|
37
|
+
const volume = cache.getVolume(volumeId) as Types.IImageVolume;
|
|
38
|
+
|
|
39
|
+
if (!(volume instanceof ImageVolume)) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'Currently, you cannot decache a volume that is not an ImageVolume. So, unfortunately, volumes such as nifti (which are basic Volume, without imageIds) cannot be decached.'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const viewportInput = {
|
|
46
|
+
viewportId,
|
|
47
|
+
type: ViewportType.STACK,
|
|
48
|
+
element,
|
|
49
|
+
defaultOptions: {
|
|
50
|
+
background,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
renderingEngine.enableElement(viewportInput);
|
|
55
|
+
|
|
56
|
+
// Get the stack viewport that was created
|
|
57
|
+
const stackViewport = <Types.IStackViewport>(
|
|
58
|
+
renderingEngine.getViewport(viewportId)
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// So here we have two scenarios that we need to handle:
|
|
62
|
+
// 1. the volume was derived from a stack and we need to decache it, this is easy
|
|
63
|
+
// since we just need purge the volume from the cache and those images will get
|
|
64
|
+
// their copy of the image back
|
|
65
|
+
// 2. It was actually a native volume and we need to decache it, this is a bit more
|
|
66
|
+
// complicated since then we need to decide on the imageIds for it to get
|
|
67
|
+
// decached to
|
|
68
|
+
const hasCachedImages = volume.imageCacheOffsetMap.size > 0;
|
|
69
|
+
// Initialize the variable to hold the final result
|
|
70
|
+
let isAllImagesCached = false;
|
|
71
|
+
|
|
72
|
+
if (hasCachedImages) {
|
|
73
|
+
// Check if every imageId in the volume is in the _imageCache
|
|
74
|
+
isAllImagesCached = volume.imageIds.every((imageId) =>
|
|
75
|
+
cache.getImage(imageId)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const volumeUsedInOtherViewports = renderingEngine
|
|
80
|
+
.getVolumeViewports()
|
|
81
|
+
.find((vp) => vp.hasVolumeId(volumeId));
|
|
82
|
+
|
|
83
|
+
volume.decache(!volumeUsedInOtherViewports && isAllImagesCached);
|
|
84
|
+
|
|
85
|
+
const stack = [...volume.imageIds].reverse();
|
|
86
|
+
|
|
87
|
+
let imageIdIndexToJump = Math.max(
|
|
88
|
+
volume.imageIds.length - imageIdIndex - 1,
|
|
89
|
+
0
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Check to see if the image is already cached or not. If it's not, we will use another
|
|
93
|
+
// nearby imageId for the first image to jump to. There seem to be a lot of side effects
|
|
94
|
+
// if we jump to an image that is not cached in stack viewport while we convert
|
|
95
|
+
// from a volume viewport. For example, if we switch back and forth between stack and volume,
|
|
96
|
+
// and then try to jump to an image that is not cached, the image will not render at
|
|
97
|
+
// all when the full volume is filled. I'm not sure why yet.
|
|
98
|
+
const imageToJump = cache.getImage(stack[imageIdIndexToJump]);
|
|
99
|
+
if (!imageToJump) {
|
|
100
|
+
let minDistance = Infinity;
|
|
101
|
+
let minDistanceIndex = null;
|
|
102
|
+
|
|
103
|
+
stack.forEach((imageId, index) => {
|
|
104
|
+
const image = cache.getImage(imageId);
|
|
105
|
+
if (image) {
|
|
106
|
+
const distance = Math.abs(imageIdIndexToJump - index);
|
|
107
|
+
if (distance < minDistance) {
|
|
108
|
+
minDistance = distance;
|
|
109
|
+
minDistanceIndex = index;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
imageIdIndexToJump = minDistanceIndex;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await stackViewport.setStack(stack, imageIdIndexToJump);
|
|
118
|
+
|
|
119
|
+
// Render the image
|
|
120
|
+
stackViewport.render();
|
|
121
|
+
|
|
122
|
+
return stackViewport;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { convertVolumeToStackViewport };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { vec3 } from 'gl-matrix';
|
|
2
|
+
import { getConfiguration, getShouldUseSharedArrayBuffer } from '../init';
|
|
3
|
+
import createFloat32SharedArray from './createFloat32SharedArray';
|
|
4
|
+
import createInt16SharedArray from './createInt16SharedArray';
|
|
5
|
+
import createUint16SharedArray from './createUInt16SharedArray';
|
|
6
|
+
import createUint8SharedArray from './createUint8SharedArray';
|
|
7
|
+
import getScalingParameters from './getScalingParameters';
|
|
8
|
+
import makeVolumeMetadata from './makeVolumeMetadata';
|
|
9
|
+
import sortImageIdsAndGetSpacing from './sortImageIdsAndGetSpacing';
|
|
10
|
+
import { ImageVolumeProps, Mat3, Point3 } from '../types';
|
|
11
|
+
import cache from '../cache';
|
|
12
|
+
import { Events } from '../enums';
|
|
13
|
+
|
|
14
|
+
function generateVolumePropsFromImageIds(
|
|
15
|
+
imageIds: string[],
|
|
16
|
+
volumeId: string
|
|
17
|
+
): ImageVolumeProps {
|
|
18
|
+
const { useNorm16Texture, preferSizeOverAccuracy } =
|
|
19
|
+
getConfiguration().rendering;
|
|
20
|
+
|
|
21
|
+
const use16BitDataType = useNorm16Texture || preferSizeOverAccuracy;
|
|
22
|
+
|
|
23
|
+
const volumeMetadata = makeVolumeMetadata(imageIds);
|
|
24
|
+
|
|
25
|
+
// For a streaming volume, the data type cannot rely on cswil to load
|
|
26
|
+
// the proper array buffer type. This is because the target buffer container
|
|
27
|
+
// must be decided ahead of time.
|
|
28
|
+
// TODO: move this logic into CSWIL to avoid logic duplication.
|
|
29
|
+
// We check if scaling parameters are negative we choose Int16 instead of
|
|
30
|
+
// Uint16 for cases where BitsAllocated is 16.
|
|
31
|
+
const imageIdIndex = Math.floor(imageIds.length / 2);
|
|
32
|
+
const imageId = imageIds[imageIdIndex];
|
|
33
|
+
const scalingParameters = getScalingParameters(imageId);
|
|
34
|
+
const hasNegativeRescale =
|
|
35
|
+
scalingParameters.rescaleIntercept < 0 ||
|
|
36
|
+
scalingParameters.rescaleSlope < 0;
|
|
37
|
+
|
|
38
|
+
// The prescale is ALWAYS used with modality LUT, so we can assume that
|
|
39
|
+
// if the rescale slope is not an integer, we need to use Float32
|
|
40
|
+
const hasFloatRescale =
|
|
41
|
+
scalingParameters.rescaleIntercept % 1 !== 0 ||
|
|
42
|
+
scalingParameters.rescaleSlope % 1 !== 0;
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
BitsAllocated,
|
|
46
|
+
PixelRepresentation,
|
|
47
|
+
PhotometricInterpretation,
|
|
48
|
+
ImageOrientationPatient,
|
|
49
|
+
PixelSpacing,
|
|
50
|
+
Columns,
|
|
51
|
+
Rows,
|
|
52
|
+
} = volumeMetadata;
|
|
53
|
+
|
|
54
|
+
const rowCosineVec = vec3.fromValues(
|
|
55
|
+
ImageOrientationPatient[0],
|
|
56
|
+
ImageOrientationPatient[1],
|
|
57
|
+
ImageOrientationPatient[2]
|
|
58
|
+
);
|
|
59
|
+
const colCosineVec = vec3.fromValues(
|
|
60
|
+
ImageOrientationPatient[3],
|
|
61
|
+
ImageOrientationPatient[4],
|
|
62
|
+
ImageOrientationPatient[5]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const scanAxisNormal = vec3.create();
|
|
66
|
+
|
|
67
|
+
vec3.cross(scanAxisNormal, rowCosineVec, colCosineVec);
|
|
68
|
+
|
|
69
|
+
const { zSpacing, origin, sortedImageIds } = sortImageIdsAndGetSpacing(
|
|
70
|
+
imageIds,
|
|
71
|
+
scanAxisNormal
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const numFrames = imageIds.length;
|
|
75
|
+
|
|
76
|
+
// Spacing goes [1] then [0], as [1] is column spacing (x) and [0] is row spacing (y)
|
|
77
|
+
const spacing = <Point3>[PixelSpacing[1], PixelSpacing[0], zSpacing];
|
|
78
|
+
const dimensions = <Point3>[Columns, Rows, numFrames];
|
|
79
|
+
const direction = [
|
|
80
|
+
...rowCosineVec,
|
|
81
|
+
...colCosineVec,
|
|
82
|
+
...scanAxisNormal,
|
|
83
|
+
] as Mat3;
|
|
84
|
+
const signed = PixelRepresentation === 1;
|
|
85
|
+
const numComponents = PhotometricInterpretation === 'RGB' ? 3 : 1;
|
|
86
|
+
const useSharedArrayBuffer = getShouldUseSharedArrayBuffer();
|
|
87
|
+
const length = dimensions[0] * dimensions[1] * dimensions[2];
|
|
88
|
+
const handleCache = (sizeInBytes) => {
|
|
89
|
+
if (!cache.isCacheable(sizeInBytes)) {
|
|
90
|
+
throw new Error(Events.CACHE_SIZE_EXCEEDED);
|
|
91
|
+
}
|
|
92
|
+
cache.decacheIfNecessaryUntilBytesAvailable(sizeInBytes);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
let scalarData, sizeInBytes;
|
|
96
|
+
switch (BitsAllocated) {
|
|
97
|
+
case 8:
|
|
98
|
+
if (signed) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
'8 Bit signed images are not yet supported by this plugin.'
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
sizeInBytes = length * numComponents;
|
|
104
|
+
handleCache(sizeInBytes);
|
|
105
|
+
scalarData = useSharedArrayBuffer
|
|
106
|
+
? createUint8SharedArray(length * numComponents)
|
|
107
|
+
: new Uint8Array(length * numComponents);
|
|
108
|
+
break;
|
|
109
|
+
|
|
110
|
+
case 16:
|
|
111
|
+
// Temporary fix for 16 bit images to use Float32
|
|
112
|
+
// until the new dicom image loader handler the conversion
|
|
113
|
+
// correctly
|
|
114
|
+
if (!use16BitDataType || hasFloatRescale) {
|
|
115
|
+
sizeInBytes = length * 4;
|
|
116
|
+
scalarData = useSharedArrayBuffer
|
|
117
|
+
? createFloat32SharedArray(length)
|
|
118
|
+
: new Float32Array(length);
|
|
119
|
+
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
sizeInBytes = length * 2;
|
|
124
|
+
if (signed || hasNegativeRescale) {
|
|
125
|
+
handleCache(sizeInBytes);
|
|
126
|
+
scalarData = useSharedArrayBuffer
|
|
127
|
+
? createInt16SharedArray(length)
|
|
128
|
+
: new Int16Array(length);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!signed && !hasNegativeRescale) {
|
|
133
|
+
handleCache(sizeInBytes);
|
|
134
|
+
scalarData = useSharedArrayBuffer
|
|
135
|
+
? createUint16SharedArray(length)
|
|
136
|
+
: new Uint16Array(length);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Default to Float32 again
|
|
141
|
+
sizeInBytes = length * 4;
|
|
142
|
+
handleCache(sizeInBytes);
|
|
143
|
+
scalarData = useSharedArrayBuffer
|
|
144
|
+
? createFloat32SharedArray(length)
|
|
145
|
+
: new Float32Array(length);
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case 24:
|
|
149
|
+
sizeInBytes = length * numComponents;
|
|
150
|
+
handleCache(sizeInBytes);
|
|
151
|
+
|
|
152
|
+
// hacky because we don't support alpha channel in dicom
|
|
153
|
+
scalarData = useSharedArrayBuffer
|
|
154
|
+
? createUint8SharedArray(length * numComponents)
|
|
155
|
+
: new Uint8Array(length * numComponents);
|
|
156
|
+
break;
|
|
157
|
+
case 32:
|
|
158
|
+
sizeInBytes = length * 4;
|
|
159
|
+
handleCache(sizeInBytes);
|
|
160
|
+
scalarData = useSharedArrayBuffer
|
|
161
|
+
? createFloat32SharedArray(length)
|
|
162
|
+
: new Float32Array(length);
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Bits allocated of ${BitsAllocated} is not defined to generate scalarData for the volume.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
dimensions,
|
|
172
|
+
spacing,
|
|
173
|
+
origin,
|
|
174
|
+
direction,
|
|
175
|
+
scalarData,
|
|
176
|
+
sizeInBytes,
|
|
177
|
+
metadata: volumeMetadata,
|
|
178
|
+
imageIds: sortedImageIds,
|
|
179
|
+
volumeId,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { generateVolumePropsFromImageIds };
|
package/src/utilities/index.ts
CHANGED
|
@@ -61,13 +61,18 @@ import decimate from './decimate';
|
|
|
61
61
|
import imageRetrieveMetadataProvider from './imageRetrieveMetadataProvider';
|
|
62
62
|
import isVideoTransferSyntax from './isVideoTransferSyntax';
|
|
63
63
|
import { getBufferConfiguration } from './getBufferConfiguration';
|
|
64
|
+
import { generateVolumePropsFromImageIds } from './generateVolumePropsFromImageIds';
|
|
65
|
+
import { convertStackToVolumeViewport } from './convertStackToVolumeViewport';
|
|
66
|
+
import { convertVolumeToStackViewport } from './convertVolumeToStackViewport';
|
|
64
67
|
import VoxelManager from './VoxelManager';
|
|
68
|
+
import roundNumber, { roundToPrecision } from './roundNumber';
|
|
65
69
|
|
|
66
70
|
// name spaces
|
|
67
71
|
import * as planar from './planar';
|
|
68
72
|
import * as windowLevel from './windowLevel';
|
|
69
73
|
import * as colormap from './colormap';
|
|
70
74
|
import * as transferFunctionUtils from './transferFunctionUtils';
|
|
75
|
+
import * as cacheUtils from './cacheUtils';
|
|
71
76
|
|
|
72
77
|
export {
|
|
73
78
|
eventListener,
|
|
@@ -139,4 +144,10 @@ export {
|
|
|
139
144
|
isVideoTransferSyntax,
|
|
140
145
|
getBufferConfiguration,
|
|
141
146
|
VoxelManager,
|
|
147
|
+
generateVolumePropsFromImageIds,
|
|
148
|
+
convertStackToVolumeViewport,
|
|
149
|
+
convertVolumeToStackViewport,
|
|
150
|
+
cacheUtils,
|
|
151
|
+
roundNumber,
|
|
152
|
+
roundToPrecision,
|
|
142
153
|
};
|
package/src/utilities/planar.ts
CHANGED
|
@@ -28,11 +28,22 @@ function linePlaneIntersection(p0: Point3, p1: Point3, plane: Plane): Point3 {
|
|
|
28
28
|
* It returns the plane equation defined by a point and a normal vector.
|
|
29
29
|
* @param normal - normal vector
|
|
30
30
|
* @param point - a point on the plane
|
|
31
|
+
* @param normalized - if true, the values of the plane equation will be normalized
|
|
31
32
|
* @returns - [A, B,C, D] of plane equation A*X + B*Y + C*Z = D
|
|
32
33
|
*/
|
|
33
|
-
function planeEquation(
|
|
34
|
+
function planeEquation(
|
|
35
|
+
normal: Point3,
|
|
36
|
+
point: Point3 | vec3,
|
|
37
|
+
normalized = false
|
|
38
|
+
): Plane {
|
|
34
39
|
const [A, B, C] = normal;
|
|
35
40
|
const D = A * point[0] + B * point[1] + C * point[2];
|
|
41
|
+
|
|
42
|
+
if (normalized) {
|
|
43
|
+
const length = Math.sqrt(A * A + B * B + C * C);
|
|
44
|
+
return [A / length, B / length, C / length, D / length];
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
return [A, B, C, D];
|
|
37
48
|
}
|
|
38
49
|
|