Skip to content

Commit ab8000d

Browse files
committed
feat(ColorTransferFunction): Add sigmoid mapping
1 parent aed71ec commit ab8000d

File tree

5 files changed

+119
-2
lines changed

5 files changed

+119
-2
lines changed

Examples/Applications/ImageViewer/index.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ function createUI(renderWindow, interactorStyle, imageSlice) {
117117

118118
function applyPreset() {
119119
const preset = vtkColorMaps.getPresetByName(presetSelector.value);
120+
lookupTable.setSigmoidGrowthRate(-4.0);
120121
lookupTable.applyColorMap(preset);
121122
lookupTable.setMappingRange(...scalars.getRange());
122123
lookupTable.updateRange();
@@ -194,12 +195,35 @@ function createUI(renderWindow, interactorStyle, imageSlice) {
194195
}
195196
interpolationSelector.addEventListener('input', updateInterpolation);
196197

198+
const mappingModeLabel = document.createElement('label');
199+
mappingModeLabel.for = 'mappingMode';
200+
mappingModeLabel.innerText = 'Mapping Sigmoid:';
201+
const mappingModeSelector = document.createElement('input');
202+
mappingModeSelector.setAttribute('class', selectorClass);
203+
mappingModeSelector.setAttribute('id', 'mappingMode');
204+
mappingModeSelector.type = 'checkbox';
205+
mappingModeSelector.checked = false;
206+
const mappingMode = document.createElement('div');
207+
mappingMode.appendChild(mappingModeLabel);
208+
mappingMode.appendChild(mappingModeSelector);
209+
210+
function updateMappingMode() {
211+
if (mappingModeSelector.checked) {
212+
lookupTable.setScalarMappingModeToSigmoid();
213+
} else {
214+
lookupTable.setScalarMappingModeToLinear();
215+
}
216+
renderWindow.getInteractor().render();
217+
}
218+
mappingModeSelector.addEventListener('input', updateMappingMode);
219+
197220
const controlContainer = document.createElement('div');
198221
controlContainer.setAttribute('class', style.control);
199222
controlContainer.appendChild(info);
200223
controlContainer.appendChild(presetSelector);
201224
controlContainer.appendChild(windowLevel);
202225
controlContainer.appendChild(interpolation);
226+
controlContainer.appendChild(mappingMode);
203227
rootControllerContainer.appendChild(controlContainer);
204228
}
205229
// ----------------------------------------------------------------------------

Sources/Rendering/Core/ColorTransferFunction/Constants.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,14 @@ export declare enum Scale {
1010
LOG10 = 1,
1111
}
1212

13+
export declare enum ScalarMappingMode {
14+
LINEAR = 0,
15+
SIGMOID = 1,
16+
}
17+
1318
declare const _default: {
1419
ColorSpace: typeof ColorSpace;
1520
Scale: typeof Scale;
21+
ScalarMappingMode: typeof ScalarMappingMode;
1622
};
1723
export default _default;

Sources/Rendering/Core/ColorTransferFunction/Constants.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ export const Scale = {
1010
LOG10: 1,
1111
};
1212

13+
export const ScalarMappingMode = {
14+
LINEAR: 0,
15+
SIGMOID: 1,
16+
};
17+
1318
export default {
1419
ColorSpace,
1520
Scale,
21+
ScalarMappingMode,
1622
};

Sources/Rendering/Core/ColorTransferFunction/index.d.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import vtkDataArray from '../../../Common/Core/DataArray';
22
import vtkScalarsToColors from '../../../Common/Core/ScalarsToColors';
3-
import { ColorSpace, Scale } from './Constants';
3+
import { ColorSpace, ScalarMappingMode, Scale } from './Constants';
44

55
export interface vtkColorTransferFunction extends vtkScalarsToColors {
66
/**
@@ -336,6 +336,48 @@ export interface vtkColorTransferFunction extends vtkScalarsToColors {
336336
* @param {Scale} scale
337337
*/
338338
setScale(scale: Scale): void;
339+
340+
/**
341+
* Get the scalar mapping mode of the color transfer function.
342+
* @returns {ScalarMappingMode}
343+
*/
344+
getScalarMappingMode(): ScalarMappingMode;
345+
346+
/**
347+
* Set the scalar mapping mode of the color transfer function.
348+
* @param {ScalarMappingMode} mode
349+
*/
350+
setScalarMappingMode(mode: ScalarMappingMode): void;
351+
352+
/**
353+
* Set the scalar mapping mode to a linear.
354+
* Scalar values will be mapped linearly to
355+
* colormap using the set mappingRange.
356+
*/
357+
setScalarMappingModeToLinear(): void;
358+
359+
/**
360+
* Set the scalar mapping mode to a sigmoid curve.
361+
* Scalar values will be mapped to colormap using
362+
* a sigmoid function using the set mappingRange.
363+
*/
364+
setScalarMappingModeToSigmoid(): void;
365+
366+
/**
367+
* Get sigmoid growth rate. This controls the
368+
* steepness of the sigmoid function used for
369+
* scalar mapping.
370+
* @returns {number}
371+
*/
372+
getSigmoidGrowthRate(): number;
373+
374+
/**
375+
* Set sigmoid growth rate. This controls the
376+
* steepness of the sigmoid function used for
377+
* scalar mapping.
378+
* @param {number} growthRate
379+
*/
380+
setSigmoidGrowthRate(growthRate: number): void;
339381
}
340382

341383
/**

Sources/Rendering/Core/ColorTransferFunction/index.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
33
import vtkScalarsToColors from 'vtk.js/Sources/Common/Core/ScalarsToColors';
44
import Constants from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction/Constants';
55

6-
const { ColorSpace, Scale } = Constants;
6+
const { ColorSpace, Scale, ScalarMappingMode } = Constants;
77
const { ScalarMappingTarget } = vtkScalarsToColors;
88
const { vtkDebugMacro, vtkErrorMacro, vtkWarningMacro } = macro;
99

@@ -493,6 +493,8 @@ function vtkColorTransferFunction(publicAPI, model) {
493493
];
494494
}
495495

496+
const useSigmoid =
497+
publicAPI.getScalarMappingMode() === ScalarMappingMode.SIGMOID;
496498
// For each table entry
497499
for (let i = 0; i < size; i++) {
498500
// Find our location in the table
@@ -501,11 +503,23 @@ function vtkColorTransferFunction(publicAPI, model) {
501503
// Find our X location. If we are taking only 1 sample, make
502504
// it halfway between start and end (usually start and end will
503505
// be the same in this case)
506+
let c = 0;
507+
let w = 0;
504508
if (size > 1) {
509+
c = (xEnd + xStart) / 2.0;
510+
w = xEnd - xStart;
505511
x = xStart + (i / (size - 1.0)) * (xEnd - xStart);
506512
} else {
513+
c = (scaledMappingRange[0] + scaledMappingRange[1]) / 2.0;
514+
w = scaledMappingRange[1] - scaledMappingRange[0];
507515
x = 0.5 * (xStart + xEnd);
508516
}
517+
if (useSigmoid) {
518+
// Apply the sigmoid function to x,
519+
const y = 1 / (1 + Math.exp((model.sigmoidGrowthRate * (x - c)) / w));
520+
// and re-interpret it as a linear parameter.
521+
x = y * w + (c - w / 2);
522+
}
509523

510524
// Linearly map x from mappingRange to [0, numberOfValues-1],
511525
// discretize (round down to the closest integer),
@@ -1246,6 +1260,21 @@ function vtkColorTransferFunction(publicAPI, model) {
12461260
}
12471261
isModified = isModified || oldNanColor !== JSON.stringify(model.nanColor);
12481262

1263+
if (colorMap.BelowRangeColor) {
1264+
if (colorMap.BelowRangeColor.length === 3) {
1265+
publicAPI.setBelowRangeColor([...colorMap.BelowRangeColor, 1]);
1266+
} else {
1267+
publicAPI.setBelowRangeColor(colorMap.BelowRangeColor);
1268+
}
1269+
}
1270+
if (colorMap.AboveRangeColor) {
1271+
if (colorMap.AboveRangeColor.length === 3) {
1272+
publicAPI.setAboveRangeColor([...colorMap.AboveRangeColor, 1]);
1273+
} else {
1274+
publicAPI.setAboveRangeColor(colorMap.AboveRangeColor);
1275+
}
1276+
}
1277+
12491278
const oldNodes = isModified || JSON.stringify(model.nodes);
12501279
if (colorMap.RGBPoints) {
12511280
const size = colorMap.RGBPoints.length;
@@ -1273,6 +1302,12 @@ function vtkColorTransferFunction(publicAPI, model) {
12731302

12741303
return modifiedInvoked || callModified;
12751304
};
1305+
1306+
publicAPI.setScalarMappingModeToLinear = () =>
1307+
publicAPI.setScalarMappingMode(ScalarMappingMode.LINEAR);
1308+
1309+
publicAPI.setScalarMappingModeToSigmoid = () =>
1310+
publicAPI.setScalarMappingMode(ScalarMappingMode.SIGMOID);
12761311
}
12771312

12781313
// ----------------------------------------------------------------------------
@@ -1284,6 +1319,7 @@ const DEFAULT_VALUES = {
12841319
colorSpace: ColorSpace.RGB,
12851320
hSVWrap: true,
12861321
scale: Scale.LINEAR,
1322+
scalarMappingMode: ScalarMappingMode.LINEAR,
12871323

12881324
nanColor: null,
12891325
belowRangeColor: null,
@@ -1301,6 +1337,7 @@ const DEFAULT_VALUES = {
13011337

13021338
discretize: false,
13031339
numberOfValues: 256,
1340+
sigmoidGrowthRate: -4,
13041341
};
13051342

13061343
// ----------------------------------------------------------------------------
@@ -1331,8 +1368,10 @@ export function extend(publicAPI, model, initialValues = {}) {
13311368
'useBelowRangeColor',
13321369
'discretize',
13331370
'numberOfValues',
1371+
'sigmoidGrowthRate',
13341372
{ type: 'enum', name: 'colorSpace', enum: ColorSpace },
13351373
{ type: 'enum', name: 'scale', enum: Scale },
1374+
{ type: 'enum', name: 'scalarMappingMode', enum: ScalarMappingMode },
13361375
]);
13371376

13381377
macro.setArray(

0 commit comments

Comments
 (0)