@cornerstonejs/core 5.0.10 → 5.0.11

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.
@@ -33,6 +33,7 @@ declare abstract class BaseVolumeViewport extends Viewport {
33
33
  private initializeVolumeNewImageEventDispatcher;
34
34
  private setVOILUTFunction;
35
35
  private setColormap;
36
+ private _applyColormapOpacity;
36
37
  private setOpacity;
37
38
  private setInvert;
38
39
  protected getVOIModifiedEventDetail(volumeId: string): VoiModifiedEventDetail;
@@ -1,5 +1,4 @@
1
1
  import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
2
- import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
3
2
  import { vec2, vec3 } from 'gl-matrix';
4
3
  import cache from '../cache/cache';
5
4
  import { MPR_CAMERA_VALUES, RENDERING_DEFAULTS, VIEWPORT_PRESETS, } from '../constants';
@@ -12,7 +11,7 @@ import * as colormapUtils from '../utilities/colormap';
12
11
  import invertRgbTransferFunction from '../utilities/invertRgbTransferFunction';
13
12
  import createSigmoidRGBTransferFunction from '../utilities/createSigmoidRGBTransferFunction';
14
13
  import transformWorldToIndex from '../utilities/transformWorldToIndex';
15
- import { findMatchingColormap, updateOpacity as colormapUpdateOpacity, updateThreshold as colormapUpdateThreshold, getThresholdValue, getMaxOpacity, } from '../utilities/colormap';
14
+ import { findMatchingColormap, updateOpacity as colormapUpdateOpacity, updateThreshold as colormapUpdateThreshold, updateOpacityMapping as colormapUpdateOpacityMapping, getOpacityState, } from '../utilities/colormap';
16
15
  import getAcquisitionPlaneOrientation from '../utilities/getAcquisitionPlaneOrientation';
17
16
  import { getTransferFunctionNodes, setTransferFunctionNodes, } from '../utilities/transferFunctionUtils';
18
17
  import createVolumeActor from './helpers/createVolumeActor';
@@ -163,10 +162,10 @@ class BaseVolumeViewport extends Viewport {
163
162
  return acc;
164
163
  }, []);
165
164
  const matchedColormap = findMatchingColormap(RGBPoints, volumeActor) || {};
166
- const threshold = getThresholdValue(volumeActor);
167
- const opacity = getMaxOpacity(volumeActor);
168
- matchedColormap.threshold = threshold;
165
+ const { opacity, opacityMapping, threshold } = getOpacityState(volumeActor);
169
166
  matchedColormap.opacity = opacity;
167
+ matchedColormap.opacityMapping = opacityMapping;
168
+ matchedColormap.threshold = threshold;
170
169
  return matchedColormap;
171
170
  };
172
171
  this.getRotation = () => {
@@ -447,6 +446,7 @@ class BaseVolumeViewport extends Viewport {
447
446
  cfun.setMappingRange(range[0], range[1]);
448
447
  volumeActor.getProperty().setRGBTransferFunction(0, cfun);
449
448
  this.viewportProperties.colormap = colormap;
449
+ this._applyColormapOpacity(colormap, volumeActor);
450
450
  if (!suppressEvents) {
451
451
  const completeColormap = this.getColormap(volumeId);
452
452
  const eventDetail = {
@@ -458,26 +458,33 @@ class BaseVolumeViewport extends Viewport {
458
458
  triggerEvent(this.element, Events.COLORMAP_MODIFIED, eventDetail);
459
459
  }
460
460
  }
461
+ _applyColormapOpacity(colormap, volumeActor) {
462
+ const mapping = Array.isArray(colormap.opacityMapping)
463
+ ? colormap.opacityMapping
464
+ : Array.isArray(colormap.opacity)
465
+ ? colormap.opacity
466
+ : undefined;
467
+ const overall = typeof colormap.opacity === 'number' ? colormap.opacity : undefined;
468
+ if (mapping !== undefined) {
469
+ colormapUpdateOpacityMapping(volumeActor, mapping, overall);
470
+ }
471
+ else if (overall !== undefined) {
472
+ colormapUpdateOpacity(volumeActor, overall);
473
+ }
474
+ }
461
475
  setOpacity(colormap, volumeId) {
462
476
  const applicableVolumeActorInfo = this._getApplicableVolumeActor(volumeId);
463
477
  if (!applicableVolumeActorInfo) {
464
478
  return;
465
479
  }
466
480
  const { volumeActor } = applicableVolumeActorInfo;
467
- const ofun = vtkPiecewiseFunction.newInstance();
468
- if (typeof colormap.opacity === 'number') {
469
- colormapUpdateOpacity(volumeActor, colormap.opacity);
470
- }
471
- else {
472
- colormap.opacity.forEach(({ opacity, value }) => {
473
- ofun.addPoint(value, opacity);
474
- });
475
- volumeActor.getProperty().setScalarOpacity(0, ofun);
476
- }
481
+ this._applyColormapOpacity(colormap, volumeActor);
477
482
  if (!this.viewportProperties.colormap) {
478
483
  this.viewportProperties.colormap = {};
479
484
  }
480
- this.viewportProperties.colormap.opacity = colormap.opacity;
485
+ const { opacity, opacityMapping } = getOpacityState(volumeActor);
486
+ this.viewportProperties.colormap.opacity = opacity;
487
+ this.viewportProperties.colormap.opacityMapping = opacityMapping;
481
488
  const matchedColormap = this.getColormap(volumeId);
482
489
  const eventDetail = {
483
490
  viewportId: this.id,
@@ -801,7 +808,7 @@ class BaseVolumeViewport extends Viewport {
801
808
  if (colormap?.name) {
802
809
  this.setColormap(colormap, volumeId, suppressEvents);
803
810
  }
804
- if (colormap?.opacity != null) {
811
+ if (colormap?.opacity != null || colormap?.opacityMapping != null) {
805
812
  this.setOpacity(colormap, volumeId);
806
813
  }
807
814
  if (colormap?.threshold != null) {
@@ -12,6 +12,7 @@ interface OpacityMapping {
12
12
  interface ColormapPublic {
13
13
  name?: string;
14
14
  opacity?: OpacityMapping[] | number;
15
- threshold?: number;
15
+ opacityMapping?: OpacityMapping[];
16
+ threshold?: number | null;
16
17
  }
17
18
  export type { ColormapRegistration, ColormapPublic, OpacityMapping };
@@ -1,4 +1,5 @@
1
1
  import type { ColormapPublic, ColormapRegistration } from '../types';
2
+ import type { OpacityMapping } from '../types/Colormap';
2
3
  declare function registerColormap(colormap: ColormapRegistration): void;
3
4
  declare function getColormap(name: any): any;
4
5
  declare function getColormapNames(): any[];
@@ -7,6 +8,12 @@ declare function findMatchingColormap(rgbPoints: any, actor: any): ColormapPubli
7
8
  export declare function setColorMapTransferFunctionForVolumeActor(volumeInfo: any): void;
8
9
  export declare function updateOpacity(volumeActor: any, newOpacity: any): void;
9
10
  export declare function updateThreshold(volumeActor: any, newThreshold: any): void;
11
+ export declare function updateOpacityMapping(volumeActor: any, mapping: OpacityMapping[], overall?: number): void;
12
+ export declare function getOpacityState(volumeActor: any): {
13
+ opacity: number;
14
+ opacityMapping?: OpacityMapping[];
15
+ threshold: number | null;
16
+ };
10
17
  declare function getThresholdValue(volumeActor: any): any;
11
18
  declare function getMaxOpacity(volumeActor: any): number;
12
19
  export { getColormap, getColormapNames, resolveColormap, registerColormap, findMatchingColormap, getThresholdValue, getMaxOpacity, };
@@ -3,6 +3,17 @@ import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransf
3
3
  import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
4
4
  import isEqual from './isEqual';
5
5
  import { actorIsA } from './actorCheck';
6
+ const opacitySpecByActor = new WeakMap();
7
+ function getOpacitySpec(volumeActor) {
8
+ const stored = opacitySpecByActor.get(volumeActor);
9
+ if (stored) {
10
+ return stored;
11
+ }
12
+ return {
13
+ overall: getMaxOpacity(volumeActor),
14
+ threshold: getThresholdValue(volumeActor),
15
+ };
16
+ }
6
17
  const _colormaps = new Map();
7
18
  function normalizeColormapName(name) {
8
19
  return name?.trim().toLowerCase();
@@ -109,12 +120,64 @@ export function setColorMapTransferFunctionForVolumeActor(volumeInfo) {
109
120
  updateOpacityWithThreshold(volumeActor, opacity, threshold);
110
121
  }
111
122
  export function updateOpacity(volumeActor, newOpacity) {
112
- const currentThreshold = getThresholdValue(volumeActor);
113
- updateOpacityWithThreshold(volumeActor, newOpacity, currentThreshold);
123
+ const spec = getOpacitySpec(volumeActor);
124
+ applyOpacitySpec(volumeActor, { ...spec, overall: newOpacity });
114
125
  }
115
126
  export function updateThreshold(volumeActor, newThreshold) {
116
- const currentOpacity = getMaxOpacity(volumeActor);
117
- updateOpacityWithThreshold(volumeActor, currentOpacity, newThreshold);
127
+ const spec = getOpacitySpec(volumeActor);
128
+ applyOpacitySpec(volumeActor, { ...spec, threshold: newThreshold });
129
+ }
130
+ export function updateOpacityMapping(volumeActor, mapping, overall) {
131
+ const spec = getOpacitySpec(volumeActor);
132
+ applyOpacitySpec(volumeActor, {
133
+ ...spec,
134
+ mapping,
135
+ ...(overall !== undefined && { overall }),
136
+ });
137
+ }
138
+ export function getOpacityState(volumeActor) {
139
+ const spec = opacitySpecByActor.get(volumeActor);
140
+ if (spec) {
141
+ return {
142
+ opacity: spec.overall,
143
+ opacityMapping: spec.mapping,
144
+ threshold: spec.threshold ?? null,
145
+ };
146
+ }
147
+ return {
148
+ opacity: getMaxOpacity(volumeActor),
149
+ opacityMapping: undefined,
150
+ threshold: getThresholdValue(volumeActor),
151
+ };
152
+ }
153
+ function applyOpacitySpec(volumeActor, spec) {
154
+ const overall = spec.overall ?? 1;
155
+ const threshold = spec.threshold ?? null;
156
+ const mapping = spec.mapping;
157
+ opacitySpecByActor.set(volumeActor, { overall, mapping, threshold });
158
+ if (mapping?.length) {
159
+ const ofun = vtkPiecewiseFunction.newInstance();
160
+ const sorted = [...mapping].sort((a, b) => a.value - b.value);
161
+ if (threshold !== null) {
162
+ const span = Math.abs(sorted[sorted.length - 1].value - sorted[0].value) || 1;
163
+ const delta = span * 0.001;
164
+ ofun.addPoint(sorted[0].value, 0);
165
+ ofun.addPoint(threshold - delta, 0);
166
+ sorted.forEach(({ value, opacity }) => {
167
+ if (value >= threshold) {
168
+ ofun.addPoint(value, opacity * overall);
169
+ }
170
+ });
171
+ }
172
+ else {
173
+ sorted.forEach(({ value, opacity }) => {
174
+ ofun.addPoint(value, opacity * overall);
175
+ });
176
+ }
177
+ volumeActor.getProperty().setScalarOpacity(0, ofun);
178
+ return;
179
+ }
180
+ updateOpacityWithThreshold(volumeActor, overall, threshold);
118
181
  }
119
182
  function updateOpacityWithThreshold(volumeActor, opacity, threshold) {
120
183
  const meta = volumeActor.getMapper().getInputData().get('voxelManager');
@@ -1 +1 @@
1
- export declare const version = "5.0.10";
1
+ export declare const version = "5.0.11";
@@ -1 +1 @@
1
- export const version = '5.0.10';
1
+ export const version = '5.0.11';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/core",
3
- "version": "5.0.10",
3
+ "version": "5.0.11",
4
4
  "description": "Cornerstone3D Core",
5
5
  "module": "./dist/esm/index.js",
6
6
  "types": "./dist/esm/index.d.ts",
@@ -84,18 +84,18 @@
84
84
  "prepublishOnly": "pnpm run build"
85
85
  },
86
86
  "dependencies": {
87
- "@cornerstonejs/utils": "5.0.10",
87
+ "@cornerstonejs/utils": "5.0.11",
88
88
  "@kitware/vtk.js": "35.5.3",
89
89
  "comlink": "4.4.2",
90
90
  "gl-matrix": "3.4.3",
91
91
  "loglevel": "1.9.2"
92
92
  },
93
93
  "devDependencies": {
94
- "@cornerstonejs/metadata": "5.0.10"
94
+ "@cornerstonejs/metadata": "5.0.11"
95
95
  },
96
96
  "peerDependencies": {
97
- "@cornerstonejs/metadata": "5.0.10",
98
- "@cornerstonejs/utils": "5.0.10"
97
+ "@cornerstonejs/metadata": "5.0.11",
98
+ "@cornerstonejs/utils": "5.0.11"
99
99
  },
100
100
  "contributors": [
101
101
  {
@@ -108,5 +108,5 @@
108
108
  "type": "individual",
109
109
  "url": "https://ohif.org/donate"
110
110
  },
111
- "gitHead": "7a0e2fd2fb15e2893e93f469d5d83ebb7e6d4f5b"
111
+ "gitHead": "40e7a86b4bc2cdee68df76f9abed9cd762c70dc6"
112
112
  }