@cornerstonejs/tools 2.9.0 → 2.10.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/esm/stateManagement/segmentation/internalAddSegmentationRepresentation.js +1 -0
- package/dist/esm/stateManagement/segmentation/polySeg/Labelmap/labelmapComputationStrategies.js +4 -2
- package/dist/esm/tools/displayTools/Labelmap/addLabelmapToElement.d.ts +5 -1
- package/dist/esm/tools/displayTools/Labelmap/addLabelmapToElement.js +17 -3
- package/dist/esm/tools/displayTools/Labelmap/addVolumesAsIndependentComponents.d.ts +10 -0
- package/dist/esm/tools/displayTools/Labelmap/addVolumesAsIndependentComponents.js +123 -0
- package/dist/esm/tools/displayTools/Labelmap/labelmapDisplay.d.ts +1 -0
- package/dist/esm/tools/displayTools/Labelmap/labelmapDisplay.js +17 -10
- package/dist/esm/tools/segmentation/SegmentSelectTool.js +0 -1
- package/dist/esm/types/SegmentationStateTypes.d.ts +3 -1
- package/dist/esm/utilities/planar/getPointInLineOfSightWithCriteria.d.ts +5 -1
- package/dist/esm/utilities/planar/getPointInLineOfSightWithCriteria.js +40 -29
- package/dist/esm/utilities/planar/index.d.ts +3 -2
- package/dist/esm/utilities/planar/index.js +3 -2
- package/package.json +3 -3
|
@@ -10,6 +10,7 @@ function internalAddSegmentationRepresentation(viewportId, representationInput)
|
|
|
10
10
|
const { segmentationId, config } = representationInput;
|
|
11
11
|
const renderingConfig = {
|
|
12
12
|
colorLUTIndex: getColorLUTIndex(config),
|
|
13
|
+
...config,
|
|
13
14
|
};
|
|
14
15
|
defaultSegmentationStateManager.addSegmentationRepresentation(viewportId, segmentationId, representationInput.type, renderingConfig);
|
|
15
16
|
if (representationInput.type === SegmentationRepresentations.Contour) {
|
package/dist/esm/stateManagement/segmentation/polySeg/Labelmap/labelmapComputationStrategies.js
CHANGED
|
@@ -35,7 +35,9 @@ export async function computeLabelmapData(segmentationId, options = {}) {
|
|
|
35
35
|
return rawLabelmapData;
|
|
36
36
|
}
|
|
37
37
|
async function computeLabelmapFromContourSegmentation(segmentationId, options = {}) {
|
|
38
|
-
const isVolume = options.viewport
|
|
38
|
+
const isVolume = options.viewport
|
|
39
|
+
? options.viewport instanceof VolumeViewport
|
|
40
|
+
: true;
|
|
39
41
|
if (isVolume && !options.viewport) {
|
|
40
42
|
throw new Error('Cannot compute labelmap from contour segmentation without providing the viewport');
|
|
41
43
|
}
|
|
@@ -55,7 +57,7 @@ async function computeLabelmapFromContourSegmentation(segmentationId, options =
|
|
|
55
57
|
}
|
|
56
58
|
async function computeLabelmapFromSurfaceSegmentation(segmentationId, options = {}) {
|
|
57
59
|
const { viewport } = options;
|
|
58
|
-
const isVolume = viewport instanceof VolumeViewport
|
|
60
|
+
const isVolume = viewport ? viewport instanceof VolumeViewport : true;
|
|
59
61
|
const segmentIndices = options.segmentIndices?.length
|
|
60
62
|
? options.segmentIndices
|
|
61
63
|
: getUniqueSegmentIndices(segmentationId);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import type { LabelmapSegmentationData } from '../../../types/LabelmapTypes';
|
|
2
|
-
|
|
2
|
+
import type { LabelmapRenderingConfig } from '../../../types/SegmentationStateTypes';
|
|
3
|
+
declare function addLabelmapToElement(element: HTMLDivElement, labelMapData: LabelmapSegmentationData, segmentationId: string, config: LabelmapRenderingConfig): Promise<void | {
|
|
4
|
+
uid: string;
|
|
5
|
+
actor: any;
|
|
6
|
+
}>;
|
|
3
7
|
export default addLabelmapToElement;
|
|
@@ -3,8 +3,9 @@ import { getCurrentLabelmapImageIdForViewport } from '../../../stateManagement/s
|
|
|
3
3
|
import { getSegmentation } from '../../../stateManagement/segmentation/getSegmentation';
|
|
4
4
|
import { triggerSegmentationDataModified, triggerSegmentationModified, } from '../../../stateManagement/segmentation/triggerSegmentationEvents';
|
|
5
5
|
import { SegmentationRepresentations } from '../../../enums';
|
|
6
|
+
import { addVolumesAsIndependentComponents } from './addVolumesAsIndependentComponents';
|
|
6
7
|
const { uuidv4 } = utilities;
|
|
7
|
-
async function addLabelmapToElement(element, labelMapData, segmentationId) {
|
|
8
|
+
async function addLabelmapToElement(element, labelMapData, segmentationId, config) {
|
|
8
9
|
const enabledElement = getEnabledElement(element);
|
|
9
10
|
const { renderingEngine, viewport } = enabledElement;
|
|
10
11
|
const { id: viewportId } = viewport;
|
|
@@ -17,15 +18,28 @@ async function addLabelmapToElement(element, labelMapData, segmentationId) {
|
|
|
17
18
|
if (!cache.getVolume(volumeId)) {
|
|
18
19
|
await _handleMissingVolume(labelMapData);
|
|
19
20
|
}
|
|
21
|
+
const blendMode = config?.blendMode ?? Enums.BlendModes.MAXIMUM_INTENSITY_BLEND;
|
|
22
|
+
const useIndependentComponents = blendMode === Enums.BlendModes.LABELMAP_EDGE_PROJECTION_BLEND;
|
|
20
23
|
const volumeInputs = [
|
|
21
24
|
{
|
|
22
25
|
volumeId,
|
|
23
26
|
visibility,
|
|
24
|
-
blendMode: Enums.BlendModes.MAXIMUM_INTENSITY_BLEND,
|
|
25
27
|
representationUID: `${segmentationId}-${SegmentationRepresentations.Labelmap}`,
|
|
28
|
+
useIndependentComponents,
|
|
29
|
+
blendMode,
|
|
26
30
|
},
|
|
27
31
|
];
|
|
28
|
-
|
|
32
|
+
if (!volumeInputs[0].useIndependentComponents) {
|
|
33
|
+
await addVolumesToViewports(renderingEngine, volumeInputs, [viewportId], immediateRender, suppressEvents);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const result = await addVolumesAsIndependentComponents({
|
|
37
|
+
viewport,
|
|
38
|
+
volumeInputs,
|
|
39
|
+
segmentationId,
|
|
40
|
+
});
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
29
43
|
}
|
|
30
44
|
else {
|
|
31
45
|
const segmentationImageId = getCurrentLabelmapImageIdForViewport(viewport.id, segmentationId);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Types } from '@cornerstonejs/core';
|
|
2
|
+
import type vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume';
|
|
3
|
+
export declare function addVolumesAsIndependentComponents({ viewport, volumeInputs, segmentationId, }: {
|
|
4
|
+
viewport: Types.IVolumeViewport;
|
|
5
|
+
volumeInputs: Types.IVolumeInput[];
|
|
6
|
+
segmentationId: string;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
uid: string;
|
|
9
|
+
actor: vtkVolume;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { cache, Enums, convertMapperToNotSharedMapper, volumeLoader, eventTarget, createVolumeActor, } from '@cornerstonejs/core';
|
|
2
|
+
import { Events, SegmentationRepresentations } from '../../../enums';
|
|
3
|
+
import { getSegmentation } from '../../../stateManagement/segmentation/getSegmentation';
|
|
4
|
+
const internalCache = new Map();
|
|
5
|
+
const load = ({ cfun, ofun, actor }) => {
|
|
6
|
+
actor.getProperty().setRGBTransferFunction(1, cfun);
|
|
7
|
+
actor.getProperty().setScalarOpacity(1, ofun);
|
|
8
|
+
};
|
|
9
|
+
export async function addVolumesAsIndependentComponents({ viewport, volumeInputs, segmentationId, }) {
|
|
10
|
+
const defaultActor = viewport.getDefaultActor();
|
|
11
|
+
const { actor } = defaultActor;
|
|
12
|
+
const { uid, callback } = defaultActor;
|
|
13
|
+
const referenceVolumeId = viewport.getVolumeId();
|
|
14
|
+
if (internalCache.get(uid)?.added) {
|
|
15
|
+
return {
|
|
16
|
+
uid,
|
|
17
|
+
actor,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const volumeInputArray = volumeInputs;
|
|
21
|
+
const firstImageVolume = cache.getVolume(volumeInputArray[0].volumeId);
|
|
22
|
+
if (!firstImageVolume) {
|
|
23
|
+
throw new Error(`imageVolume with id: ${firstImageVolume.volumeId} does not exist`);
|
|
24
|
+
}
|
|
25
|
+
const { volumeId } = volumeInputArray[0];
|
|
26
|
+
const segImageVolume = await volumeLoader.loadVolume(volumeId);
|
|
27
|
+
if (!segImageVolume) {
|
|
28
|
+
throw new Error(`segImageVolume with id: ${segImageVolume.volumeId} does not exist`);
|
|
29
|
+
}
|
|
30
|
+
const segVoxelManager = segImageVolume.voxelManager;
|
|
31
|
+
const { imageData: segImageData } = segImageVolume;
|
|
32
|
+
const baseVolume = cache.getVolume(referenceVolumeId);
|
|
33
|
+
const baseVoxelManager = baseVolume.voxelManager;
|
|
34
|
+
const newComp = 2;
|
|
35
|
+
const cubeData = new Float32Array(newComp * baseVolume.voxelManager.getScalarDataLength());
|
|
36
|
+
const dims = segImageData.getDimensions();
|
|
37
|
+
for (let z = 0; z < dims[2]; ++z) {
|
|
38
|
+
for (let y = 0; y < dims[1]; ++y) {
|
|
39
|
+
for (let x = 0; x < dims[0]; ++x) {
|
|
40
|
+
const iTuple = x + dims[0] * (y + dims[1] * z);
|
|
41
|
+
cubeData[iTuple * newComp + 0] = baseVoxelManager.getAtIndex(iTuple);
|
|
42
|
+
cubeData[iTuple * newComp + 1] = segVoxelManager.getAtIndex(iTuple);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
viewport.removeActors([uid]);
|
|
47
|
+
const oldMapper = actor.getMapper();
|
|
48
|
+
const mapper = convertMapperToNotSharedMapper(oldMapper);
|
|
49
|
+
actor.setMapper(mapper);
|
|
50
|
+
mapper.setBlendMode(Enums.BlendModes.LABELMAP_EDGE_PROJECTION_BLEND);
|
|
51
|
+
const arrayAgain = mapper.getInputData().getPointData().getArray(0);
|
|
52
|
+
arrayAgain.setData(cubeData);
|
|
53
|
+
arrayAgain.setNumberOfComponents(2);
|
|
54
|
+
actor.getProperty().setColorMixPreset(1);
|
|
55
|
+
actor.getProperty().setForceNearestInterpolation(1, true);
|
|
56
|
+
actor.getProperty().setIndependentComponents(true);
|
|
57
|
+
viewport.addActor({
|
|
58
|
+
actor,
|
|
59
|
+
uid,
|
|
60
|
+
callback,
|
|
61
|
+
referencedId: referenceVolumeId,
|
|
62
|
+
representationUID: `${segmentationId}-${SegmentationRepresentations.Labelmap}`,
|
|
63
|
+
});
|
|
64
|
+
internalCache.set(uid, {
|
|
65
|
+
added: true,
|
|
66
|
+
segmentationRepresentationUID: `${segmentationId}`,
|
|
67
|
+
originalBlendMode: viewport.getBlendMode(),
|
|
68
|
+
});
|
|
69
|
+
actor.set({
|
|
70
|
+
preLoad: load,
|
|
71
|
+
});
|
|
72
|
+
function onSegmentationDataModified(evt) {
|
|
73
|
+
const { segmentationId, modifiedSlicesToUse } = evt.detail;
|
|
74
|
+
const { representationData } = getSegmentation(segmentationId);
|
|
75
|
+
const { volumeId: segVolumeId } = representationData.Labelmap;
|
|
76
|
+
if (segVolumeId !== segImageVolume.volumeId) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const segmentationVolume = cache.getVolume(segVolumeId);
|
|
80
|
+
const segVoxelManager = segmentationVolume.voxelManager;
|
|
81
|
+
const array = mapper.getInputData().getPointData().getArray(0);
|
|
82
|
+
const baseData = array.getData();
|
|
83
|
+
const newComp = 2;
|
|
84
|
+
const dims = segImageData.getDimensions();
|
|
85
|
+
const slices = modifiedSlicesToUse?.length
|
|
86
|
+
? modifiedSlicesToUse
|
|
87
|
+
: Array.from({ length: dims[2] }, (_, i) => i);
|
|
88
|
+
for (const z of slices) {
|
|
89
|
+
for (let y = 0; y < dims[1]; ++y) {
|
|
90
|
+
for (let x = 0; x < dims[0]; ++x) {
|
|
91
|
+
const iTuple = x + dims[0] * (y + dims[1] * z);
|
|
92
|
+
baseData[iTuple * newComp + 1] = segVoxelManager.getAtIndex(iTuple);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
array.setData(baseData);
|
|
97
|
+
}
|
|
98
|
+
eventTarget.addEventListener(Events.SEGMENTATION_DATA_MODIFIED, onSegmentationDataModified);
|
|
99
|
+
eventTarget.addEventListener(Events.SEGMENTATION_REPRESENTATION_REMOVED, async (evt) => {
|
|
100
|
+
eventTarget.removeEventListener(Events.SEGMENTATION_DATA_MODIFIED, onSegmentationDataModified);
|
|
101
|
+
const actorEntry = viewport.getActor(uid);
|
|
102
|
+
const { element, id } = viewport;
|
|
103
|
+
viewport.removeActors([uid]);
|
|
104
|
+
const actor = await createVolumeActor({
|
|
105
|
+
volumeId: uid,
|
|
106
|
+
blendMode: Enums.BlendModes.MAXIMUM_INTENSITY_BLEND,
|
|
107
|
+
callback: ({ volumeActor }) => {
|
|
108
|
+
if (actorEntry.callback) {
|
|
109
|
+
actorEntry.callback({
|
|
110
|
+
volumeActor,
|
|
111
|
+
volumeId,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
}, element, id);
|
|
116
|
+
viewport.addActor({ actor, uid });
|
|
117
|
+
viewport.render();
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
uid,
|
|
121
|
+
actor,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Types } from '@cornerstonejs/core';
|
|
2
2
|
import type { LabelmapRepresentation } from '../../../types/SegmentationStateTypes';
|
|
3
|
+
export declare const MAX_NUMBER_COLORS = 255;
|
|
3
4
|
declare function removeRepresentation(viewportId: string, segmentationId: string, renderImmediate?: boolean): void;
|
|
4
5
|
declare function render(viewport: Types.IStackViewport | Types.IVolumeViewport, representation: LabelmapRepresentation): Promise<void>;
|
|
5
6
|
declare const _default: {
|
|
@@ -12,7 +12,7 @@ import SegmentationRepresentations from '../../../enums/SegmentationRepresentati
|
|
|
12
12
|
import { internalGetHiddenSegmentIndices } from '../../../stateManagement/segmentation/helpers/internalGetHiddenSegmentIndices';
|
|
13
13
|
import { getActiveSegmentIndex } from '../../../stateManagement/segmentation/getActiveSegmentIndex';
|
|
14
14
|
import { getLabelmapActorEntry } from '../../../stateManagement/segmentation/helpers/getSegmentationActor';
|
|
15
|
-
const MAX_NUMBER_COLORS = 255;
|
|
15
|
+
export const MAX_NUMBER_COLORS = 255;
|
|
16
16
|
const labelMapConfigCache = new Map();
|
|
17
17
|
let polySegConversionInProgress = false;
|
|
18
18
|
function removeRepresentation(viewportId, segmentationId, renderImmediate = false) {
|
|
@@ -33,7 +33,7 @@ function removeRepresentation(viewportId, segmentationId, renderImmediate = fals
|
|
|
33
33
|
viewport.render();
|
|
34
34
|
}
|
|
35
35
|
async function render(viewport, representation) {
|
|
36
|
-
const { segmentationId } = representation;
|
|
36
|
+
const { segmentationId, config } = representation;
|
|
37
37
|
const segmentation = getSegmentation(segmentationId);
|
|
38
38
|
if (!segmentation) {
|
|
39
39
|
console.warn('No segmentation found for segmentationId: ', segmentationId);
|
|
@@ -58,7 +58,7 @@ async function render(viewport, representation) {
|
|
|
58
58
|
}
|
|
59
59
|
if (viewport instanceof VolumeViewport) {
|
|
60
60
|
if (!labelmapActorEntry) {
|
|
61
|
-
await _addLabelmapToViewport(viewport, labelmapData, segmentationId);
|
|
61
|
+
await _addLabelmapToViewport(viewport, labelmapData, segmentationId, config);
|
|
62
62
|
}
|
|
63
63
|
labelmapActorEntry = getLabelmapActorEntry(viewport.id, segmentationId);
|
|
64
64
|
}
|
|
@@ -68,7 +68,7 @@ async function render(viewport, representation) {
|
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
if (!labelmapActorEntry) {
|
|
71
|
-
await _addLabelmapToViewport(viewport, labelmapData, segmentationId);
|
|
71
|
+
await _addLabelmapToViewport(viewport, labelmapData, segmentationId, config);
|
|
72
72
|
}
|
|
73
73
|
labelmapActorEntry = getLabelmapActorEntry(viewport.id, segmentationId);
|
|
74
74
|
}
|
|
@@ -133,11 +133,17 @@ function _setLabelmapColorAndOpacity(viewportId, labelmapActorEntry, segmentatio
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
-
const labelmapActor = labelmapActorEntry.actor;
|
|
137
|
-
labelmapActor.getProperty().setRGBTransferFunction(0, cfun);
|
|
138
136
|
ofun.setClamping(false);
|
|
139
|
-
labelmapActor.
|
|
140
|
-
labelmapActor.
|
|
137
|
+
const labelmapActor = labelmapActorEntry.actor;
|
|
138
|
+
const { preLoad } = labelmapActor.get('preLoad') || { preLoad: null };
|
|
139
|
+
if (preLoad) {
|
|
140
|
+
preLoad({ cfun, ofun, actor: labelmapActor });
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
labelmapActor.getProperty().setRGBTransferFunction(0, cfun);
|
|
144
|
+
labelmapActor.getProperty().setScalarOpacity(0, ofun);
|
|
145
|
+
labelmapActor.getProperty().setInterpolationTypeToNearest();
|
|
146
|
+
}
|
|
141
147
|
if (renderOutline) {
|
|
142
148
|
labelmapActor.getProperty().setUseLabelOutline(renderOutline);
|
|
143
149
|
labelmapActor.getProperty().setLabelOutlineOpacity(outlineOpacity);
|
|
@@ -241,8 +247,9 @@ function _needsTransferFunctionUpdate(viewportId, segmentationId, segmentIndex,
|
|
|
241
247
|
forceColorUpdate,
|
|
242
248
|
};
|
|
243
249
|
}
|
|
244
|
-
async function _addLabelmapToViewport(viewport, labelmapData, segmentationId) {
|
|
245
|
-
await addLabelmapToElement(viewport.element, labelmapData, segmentationId);
|
|
250
|
+
async function _addLabelmapToViewport(viewport, labelmapData, segmentationId, config) {
|
|
251
|
+
const result = await addLabelmapToElement(viewport.element, labelmapData, segmentationId, config);
|
|
252
|
+
return result || undefined;
|
|
246
253
|
}
|
|
247
254
|
export default {
|
|
248
255
|
render,
|
|
@@ -7,7 +7,6 @@ import RepresentationTypes from '../../enums/SegmentationRepresentations';
|
|
|
7
7
|
import { setActiveSegmentIndex } from '../../stateManagement/segmentation/segmentIndex';
|
|
8
8
|
import { getHoveredContourSegmentationAnnotation, getSegmentIndexAtLabelmapBorder, getSegmentIndexAtWorldPoint, } from '../../utilities/segmentation';
|
|
9
9
|
import { state } from '../../store/state';
|
|
10
|
-
import SegmentationRepresentations from '../../enums/SegmentationRepresentations';
|
|
11
10
|
class SegmentSelectTool extends BaseTool {
|
|
12
11
|
static { this.SelectMode = {
|
|
13
12
|
Inside: 'Inside',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Types } from '@cornerstonejs/core';
|
|
1
|
+
import type { Enums as coreEnums, Types } from '@cornerstonejs/core';
|
|
2
2
|
import type * as Enums from '../enums';
|
|
3
3
|
import type { ContourSegmentationData } from './ContourTypes';
|
|
4
4
|
import type { LabelmapSegmentationData } from './LabelmapTypes';
|
|
@@ -35,6 +35,7 @@ export type LabelmapRenderingConfig = {
|
|
|
35
35
|
cfun: vtkColorTransferFunction;
|
|
36
36
|
ofun: vtkPiecewiseFunction;
|
|
37
37
|
colorLUTIndex: number;
|
|
38
|
+
blendMode?: coreEnums.BlendModes;
|
|
38
39
|
};
|
|
39
40
|
export type ContourRenderingConfig = {};
|
|
40
41
|
export type SurfaceRenderingConfig = {};
|
|
@@ -89,6 +90,7 @@ export type RepresentationPublicInput = {
|
|
|
89
90
|
type?: Enums.SegmentationRepresentations;
|
|
90
91
|
config?: {
|
|
91
92
|
colorLUTOrIndex?: Types.ColorLUT | number;
|
|
93
|
+
blendMode?: coreEnums.BlendModes;
|
|
92
94
|
};
|
|
93
95
|
};
|
|
94
96
|
export {};
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import type { Types } from '@cornerstonejs/core';
|
|
2
|
-
export
|
|
2
|
+
export declare function getPointInLineOfSightWithCriteria(viewport: Types.IVolumeViewport, worldPos: Types.Point3, targetVolumeId: string, criteriaFunction: (intensity: number, point: Types.Point3) => Types.Point3, stepSize?: number): Types.Point3;
|
|
3
|
+
export declare function getPointsInLineOfSight(viewport: Types.IVolumeViewport, worldPos: Types.Point3, { targetVolumeId, stepSize }: {
|
|
4
|
+
targetVolumeId: string;
|
|
5
|
+
stepSize: number;
|
|
6
|
+
}): Types.Point3[];
|
|
@@ -1,38 +1,49 @@
|
|
|
1
|
-
import vtkMath from '@kitware/vtk.js/Common/Core/Math';
|
|
2
1
|
import { utilities as csUtils } from '@cornerstonejs/core';
|
|
3
|
-
export
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const bounds = viewport.getBounds();
|
|
9
|
-
const xMin = bounds[0];
|
|
10
|
-
const xMax = bounds[1];
|
|
11
|
-
const vector = [0, 0, 0];
|
|
12
|
-
let point = [0, 0, 0];
|
|
13
|
-
vtkMath.subtract(worldPos, cameraPosition, vector);
|
|
2
|
+
export function getPointInLineOfSightWithCriteria(viewport, worldPos, targetVolumeId, criteriaFunction, stepSize = 0.25) {
|
|
3
|
+
const points = getPointsInLineOfSight(viewport, worldPos, {
|
|
4
|
+
targetVolumeId,
|
|
5
|
+
stepSize,
|
|
6
|
+
});
|
|
14
7
|
let pickedPoint;
|
|
15
|
-
for (
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (_inBounds(point, bounds)) {
|
|
21
|
-
const intensity = viewport.getIntensityFromWorld(point);
|
|
22
|
-
const pointToPick = criteriaFunction(intensity, point);
|
|
23
|
-
if (pointToPick) {
|
|
24
|
-
pickedPoint = pointToPick;
|
|
25
|
-
}
|
|
8
|
+
for (const point of points) {
|
|
9
|
+
const intensity = viewport.getIntensityFromWorld(point);
|
|
10
|
+
const pointToPick = criteriaFunction(intensity, point);
|
|
11
|
+
if (pointToPick) {
|
|
12
|
+
pickedPoint = pointToPick;
|
|
26
13
|
}
|
|
27
14
|
}
|
|
28
15
|
return pickedPoint;
|
|
29
16
|
}
|
|
17
|
+
export function getPointsInLineOfSight(viewport, worldPos, { targetVolumeId, stepSize }) {
|
|
18
|
+
const camera = viewport.getCamera();
|
|
19
|
+
const { viewPlaneNormal: normalDirection } = camera;
|
|
20
|
+
const { spacingInNormalDirection } = csUtils.getTargetVolumeAndSpacingInNormalDir(viewport, camera, targetVolumeId);
|
|
21
|
+
const step = spacingInNormalDirection * stepSize || 1;
|
|
22
|
+
const bounds = viewport.getBounds();
|
|
23
|
+
const points = [];
|
|
24
|
+
let currentPos = [...worldPos];
|
|
25
|
+
while (_inBounds(currentPos, bounds)) {
|
|
26
|
+
points.push([...currentPos]);
|
|
27
|
+
currentPos[0] += normalDirection[0] * step;
|
|
28
|
+
currentPos[1] += normalDirection[1] * step;
|
|
29
|
+
currentPos[2] += normalDirection[2] * step;
|
|
30
|
+
}
|
|
31
|
+
currentPos = [...worldPos];
|
|
32
|
+
while (_inBounds(currentPos, bounds)) {
|
|
33
|
+
points.push([...currentPos]);
|
|
34
|
+
currentPos[0] -= normalDirection[0] * step;
|
|
35
|
+
currentPos[1] -= normalDirection[1] * step;
|
|
36
|
+
currentPos[2] -= normalDirection[2] * step;
|
|
37
|
+
}
|
|
38
|
+
return points;
|
|
39
|
+
}
|
|
30
40
|
const _inBounds = function (point, bounds) {
|
|
31
41
|
const [xMin, xMax, yMin, yMax, zMin, zMax] = bounds;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
point[
|
|
35
|
-
point[1]
|
|
36
|
-
point[
|
|
37
|
-
point[2]
|
|
42
|
+
const padding = 10;
|
|
43
|
+
return (point[0] > xMin + padding &&
|
|
44
|
+
point[0] < xMax - padding &&
|
|
45
|
+
point[1] > yMin + padding &&
|
|
46
|
+
point[1] < yMax - padding &&
|
|
47
|
+
point[2] > zMin + padding &&
|
|
48
|
+
point[2] < zMax - padding);
|
|
38
49
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import filterAnnotationsWithinSlice from './filterAnnotationsWithinSlice';
|
|
2
2
|
import getWorldWidthAndHeightFromCorners from './getWorldWidthAndHeightFromCorners';
|
|
3
3
|
import filterAnnotationsForDisplay from './filterAnnotationsForDisplay';
|
|
4
|
-
import getPointInLineOfSightWithCriteria from './getPointInLineOfSightWithCriteria';
|
|
4
|
+
import { getPointInLineOfSightWithCriteria, getPointsInLineOfSight } from './getPointInLineOfSightWithCriteria';
|
|
5
5
|
import { isPlaneIntersectingAABB } from './isPlaneIntersectingAABB';
|
|
6
6
|
import { filterAnnotationsWithinSamePlane } from './filterAnnotationsWithinPlane';
|
|
7
7
|
declare const _default: {
|
|
@@ -11,6 +11,7 @@ declare const _default: {
|
|
|
11
11
|
getPointInLineOfSightWithCriteria: typeof getPointInLineOfSightWithCriteria;
|
|
12
12
|
isPlaneIntersectingAABB: (origin: any, normal: any, minX: any, minY: any, minZ: any, maxX: any, maxY: any, maxZ: any) => boolean;
|
|
13
13
|
filterAnnotationsWithinSamePlane: typeof filterAnnotationsWithinSamePlane;
|
|
14
|
+
getPointsInLineOfSight: typeof getPointsInLineOfSight;
|
|
14
15
|
};
|
|
15
16
|
export default _default;
|
|
16
|
-
export { filterAnnotationsWithinSlice, getWorldWidthAndHeightFromCorners, filterAnnotationsForDisplay, getPointInLineOfSightWithCriteria, isPlaneIntersectingAABB, filterAnnotationsWithinSamePlane, };
|
|
17
|
+
export { filterAnnotationsWithinSlice, getWorldWidthAndHeightFromCorners, filterAnnotationsForDisplay, getPointInLineOfSightWithCriteria, isPlaneIntersectingAABB, filterAnnotationsWithinSamePlane, getPointsInLineOfSight, };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import filterAnnotationsWithinSlice from './filterAnnotationsWithinSlice';
|
|
2
2
|
import getWorldWidthAndHeightFromCorners from './getWorldWidthAndHeightFromCorners';
|
|
3
3
|
import filterAnnotationsForDisplay from './filterAnnotationsForDisplay';
|
|
4
|
-
import getPointInLineOfSightWithCriteria from './getPointInLineOfSightWithCriteria';
|
|
4
|
+
import { getPointInLineOfSightWithCriteria, getPointsInLineOfSight, } from './getPointInLineOfSightWithCriteria';
|
|
5
5
|
import { isPlaneIntersectingAABB } from './isPlaneIntersectingAABB';
|
|
6
6
|
import { filterAnnotationsWithinSamePlane } from './filterAnnotationsWithinPlane';
|
|
7
7
|
export default {
|
|
@@ -11,5 +11,6 @@ export default {
|
|
|
11
11
|
getPointInLineOfSightWithCriteria,
|
|
12
12
|
isPlaneIntersectingAABB,
|
|
13
13
|
filterAnnotationsWithinSamePlane,
|
|
14
|
+
getPointsInLineOfSight,
|
|
14
15
|
};
|
|
15
|
-
export { filterAnnotationsWithinSlice, getWorldWidthAndHeightFromCorners, filterAnnotationsForDisplay, getPointInLineOfSightWithCriteria, isPlaneIntersectingAABB, filterAnnotationsWithinSamePlane, };
|
|
16
|
+
export { filterAnnotationsWithinSlice, getWorldWidthAndHeightFromCorners, filterAnnotationsForDisplay, getPointInLineOfSightWithCriteria, isPlaneIntersectingAABB, filterAnnotationsWithinSamePlane, getPointsInLineOfSight, };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/tools",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "Cornerstone3D Tools",
|
|
5
5
|
"types": "./dist/esm/index.d.ts",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"canvas": "^2.11.2"
|
|
105
105
|
},
|
|
106
106
|
"peerDependencies": {
|
|
107
|
-
"@cornerstonejs/core": "^2.
|
|
107
|
+
"@cornerstonejs/core": "^2.10.0",
|
|
108
108
|
"@kitware/vtk.js": "32.1.1",
|
|
109
109
|
"@types/d3-array": "^3.0.4",
|
|
110
110
|
"@types/d3-interpolate": "^3.0.1",
|
|
@@ -123,5 +123,5 @@
|
|
|
123
123
|
"type": "individual",
|
|
124
124
|
"url": "https://ohif.org/donate"
|
|
125
125
|
},
|
|
126
|
-
"gitHead": "
|
|
126
|
+
"gitHead": "aab86e3f13a86bcd2d975a4cfbdf7eee7fa1627a"
|
|
127
127
|
}
|