@kitware/vtk.js
Version:
Visualization Toolkit for the Web
368 lines (331 loc) • 12.7 kB
JavaScript
import { m as macro } from '../../macros2.js';
import { i as isNan, h as hsv2rgb } from './Math/index.js';
import vtkScalarsToColors from './ScalarsToColors.js';
import { ScalarMappingTarget } from './ScalarsToColors/Constants.js';
import { VtkDataTypes } from './DataArray/Constants.js';
const {
vtkErrorMacro
} = macro;
// ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------
// Add module-level functions or api that you want to expose statically via
// the next section...
// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------
const BELOW_RANGE_COLOR_INDEX = 0;
const ABOVE_RANGE_COLOR_INDEX = 1;
const NAN_COLOR_INDEX = 2;
// ----------------------------------------------------------------------------
// vtkMyClass methods
// ----------------------------------------------------------------------------
function vtkLookupTable(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkLookupTable');
//----------------------------------------------------------------------------
// Description:
// Return true if all of the values defining the mapping have an opacity
// equal to 1. Default implementation return true.
publicAPI.isOpaque = () => {
if (model.opaqueFlagBuildTime.getMTime() < publicAPI.getMTime()) {
let opaque = true;
if (model.nanColor[3] < 1.0) {
opaque = 0;
}
if (model.useBelowRangeColor && model.belowRangeColor[3] < 1.0) {
opaque = 0;
}
if (model.useAboveRangeColor && model.aboveRangeColor[3] < 1.0) {
opaque = 0;
}
for (let i = 3; i < model.table.length && opaque; i += 4) {
if (model.table[i] < 255) {
opaque = false;
}
}
model.opaqueFlag = opaque;
model.opaqueFlagBuildTime.modified();
}
return model.opaqueFlag;
};
publicAPI.usingLogScale = () => false;
//----------------------------------------------------------------------------
// Don't count special colors (min, max, NaN) as available colors
publicAPI.getNumberOfAvailableColors = () => model.table.length / 4 - 3;
//----------------------------------------------------------------------------
// Apply shift/scale to the scalar value v and return the index.
publicAPI.linearIndexLookup = (v, p) => {
let dIndex = 0;
const nv = Number(v);
if (nv < p.range[0]) {
dIndex = p.maxIndex + BELOW_RANGE_COLOR_INDEX + 1.5;
} else if (nv > p.range[1]) {
dIndex = p.maxIndex + ABOVE_RANGE_COLOR_INDEX + 1.5;
} else {
dIndex = (nv + p.shift) * p.scale;
// This conditional is needed because when v is very close to
// p.Range[1], it may map above p.MaxIndex in the linear mapping
// above.
dIndex = dIndex < p.maxIndex ? dIndex : p.maxIndex;
}
return Math.floor(dIndex);
};
publicAPI.linearLookup = (v, table, p) => {
let index = 0;
if (isNan(v)) {
index = Math.floor(p.maxIndex + 1.5 + NAN_COLOR_INDEX);
} else {
index = publicAPI.linearIndexLookup(v, p);
}
const offset = 4 * index;
return table.slice(offset, offset + 4);
};
publicAPI.indexedLookupFunction = (v, table, p) => {
let index = publicAPI.getAnnotatedValueIndexInternal(v);
if (index === -1) {
index = model.numberOfColors + NAN_COLOR_INDEX;
}
const offset = 4 * index;
return [table[offset], table[offset + 1], table[offset + 2], table[offset + 3]];
};
//----------------------------------------------------------------------------
publicAPI.lookupShiftAndScale = (range, p) => {
p.shift = -range[0];
p.scale = Number.MAX_VALUE;
if (range[1] > range[0]) {
p.scale = (p.maxIndex + 1) / (range[1] - range[0]);
}
};
// Public API methods
publicAPI.mapScalarsThroughTable = (input, output, outFormat, inputOffset) => {
let lookupFunc = publicAPI.linearLookup;
if (model.indexedLookup) {
lookupFunc = publicAPI.indexedLookupFunction;
}
const trange = publicAPI.getMappingRange();
const p = {
maxIndex: publicAPI.getNumberOfColors() - 1,
range: trange,
shift: 0.0,
scale: 0.0
};
publicAPI.lookupShiftAndScale(trange, p);
const alpha = publicAPI.getAlpha();
const length = input.getNumberOfTuples();
const inIncr = input.getNumberOfComponents();
const outputV = output.getData();
const inputV = input.getData();
if (alpha >= 1.0) {
if (outFormat === ScalarMappingTarget.RGBA) {
for (let i = 0; i < length; i++) {
const cptr = lookupFunc(inputV[i * inIncr + inputOffset], model.table, p);
outputV[i * 4] = cptr[0];
outputV[i * 4 + 1] = cptr[1];
outputV[i * 4 + 2] = cptr[2];
outputV[i * 4 + 3] = cptr[3];
}
}
} else {
/* eslint-disable no-lonely-if */
if (outFormat === ScalarMappingTarget.RGBA) {
for (let i = 0; i < length; i++) {
const cptr = lookupFunc(inputV[i * inIncr + inputOffset], model.table, p);
outputV[i * 4] = cptr[0];
outputV[i * 4 + 1] = cptr[1];
outputV[i * 4 + 2] = cptr[2];
outputV[i * 4 + 3] = Math.floor(cptr[3] * alpha + 0.5);
}
}
} // alpha blending
};
publicAPI.forceBuild = () => {
let hinc = 0.0;
let sinc = 0.0;
let vinc = 0.0;
let ainc = 0.0;
const maxIndex = model.numberOfColors - 1;
if (maxIndex) {
hinc = (model.hueRange[1] - model.hueRange[0]) / maxIndex;
sinc = (model.saturationRange[1] - model.saturationRange[0]) / maxIndex;
vinc = (model.valueRange[1] - model.valueRange[0]) / maxIndex;
ainc = (model.alphaRange[1] - model.alphaRange[0]) / maxIndex;
}
model.table.length = 4 * maxIndex + 16;
const hsv = [];
const rgba = [];
for (let i = 0; i <= maxIndex; i++) {
hsv[0] = model.hueRange[0] + i * hinc;
hsv[1] = model.saturationRange[0] + i * sinc;
hsv[2] = model.valueRange[0] + i * vinc;
hsv2rgb(hsv, rgba);
rgba[3] = model.alphaRange[0] + i * ainc;
// case VTK_RAMP_LINEAR:
model.table[i * 4] = rgba[0] * 255.0 + 0.5;
model.table[i * 4 + 1] = rgba[1] * 255.0 + 0.5;
model.table[i * 4 + 2] = rgba[2] * 255.0 + 0.5;
model.table[i * 4 + 3] = rgba[3] * 255.0 + 0.5;
}
publicAPI.buildSpecialColors();
model.buildTime.modified();
};
publicAPI.setTable = table => {
// Handle JS array (assume 2D array)
if (Array.isArray(table)) {
const nbComponents = table[0].length;
model.numberOfColors = table.length;
const colorOffset = 4 - nbComponents;
let offset = 0;
// fill table
for (let i = 0; i < model.numberOfColors; i++) {
model.table[i * 4] = 255;
model.table[i * 4 + 1] = 255;
model.table[i * 4 + 2] = 255;
model.table[i * 4 + 3] = 255;
}
// extract colors
for (let i = 0; i < table.length; i++) {
const color = table[i];
for (let j = 0; j < nbComponents; j++) {
model.table[offset++] = color[j];
}
offset += colorOffset;
}
publicAPI.buildSpecialColors();
model.insertTime.modified();
publicAPI.modified();
return true;
}
if (table.getNumberOfComponents() !== 4) {
vtkErrorMacro('Expected 4 components for RGBA colors');
return false;
}
if (table.getDataType() !== VtkDataTypes.UNSIGNED_CHAR) {
vtkErrorMacro('Expected unsigned char values for RGBA colors');
return false;
}
model.numberOfColors = table.getNumberOfTuples();
const data = table.getData();
model.table.length = data.length;
for (let i = 0; i < data.length; i++) {
model.table[i] = data[i];
}
publicAPI.buildSpecialColors();
model.insertTime.modified();
publicAPI.modified();
return true;
};
publicAPI.buildSpecialColors = () => {
// Add "special" colors (NaN, below range, above range) to table here.
const {
numberOfColors
} = model;
const tptr = model.table;
let base = (numberOfColors + BELOW_RANGE_COLOR_INDEX) * 4;
// Below range color
if (model.useBelowRangeColor || numberOfColors === 0) {
tptr[base] = model.belowRangeColor[0] * 255.0 + 0.5;
tptr[base + 1] = model.belowRangeColor[1] * 255.0 + 0.5;
tptr[base + 2] = model.belowRangeColor[2] * 255.0 + 0.5;
tptr[base + 3] = model.belowRangeColor[3] * 255.0 + 0.5;
} else {
// Duplicate the first color in the table.
tptr[base] = tptr[0];
tptr[base + 1] = tptr[1];
tptr[base + 2] = tptr[2];
tptr[base + 3] = tptr[3];
}
// Above range color
base = (numberOfColors + ABOVE_RANGE_COLOR_INDEX) * 4;
if (model.useAboveRangeColor || numberOfColors === 0) {
tptr[base] = model.aboveRangeColor[0] * 255.0 + 0.5;
tptr[base + 1] = model.aboveRangeColor[1] * 255.0 + 0.5;
tptr[base + 2] = model.aboveRangeColor[2] * 255.0 + 0.5;
tptr[base + 3] = model.aboveRangeColor[3] * 255.0 + 0.5;
} else {
// Duplicate the last color in the table.
tptr[base] = tptr[4 * (numberOfColors - 1) + 0];
tptr[base + 1] = tptr[4 * (numberOfColors - 1) + 1];
tptr[base + 2] = tptr[4 * (numberOfColors - 1) + 2];
tptr[base + 3] = tptr[4 * (numberOfColors - 1) + 3];
}
// Always use NanColor
base = (numberOfColors + NAN_COLOR_INDEX) * 4;
tptr[base] = model.nanColor[0] * 255.0 + 0.5;
tptr[base + 1] = model.nanColor[1] * 255.0 + 0.5;
tptr[base + 2] = model.nanColor[2] * 255.0 + 0.5;
tptr[base + 3] = model.nanColor[3] * 255.0 + 0.5;
};
publicAPI.build = () => {
if (model.table.length < 1 || publicAPI.getMTime() > model.buildTime.getMTime() && model.insertTime.getMTime() <= model.buildTime.getMTime()) {
publicAPI.forceBuild();
}
};
if (model.table.length > 0) {
// Ensure that special colors are properly included in the table
publicAPI.buildSpecialColors();
// ensure insertTime is more recently modified than buildTime if
// a table is provided via the constructor
model.insertTime.modified();
}
}
// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
const DEFAULT_VALUES = {
numberOfColors: 256,
// table: null,
hueRange: [0.0, 0.66667],
saturationRange: [1.0, 1.0],
valueRange: [1.0, 1.0],
alphaRange: [1.0, 1.0],
nanColor: [0.5, 0.0, 0.0, 1.0],
belowRangeColor: [0.0, 0.0, 0.0, 1.0],
aboveRangeColor: [1.0, 1.0, 1.0, 1.0],
useAboveRangeColor: false,
useBelowRangeColor: false,
alpha: 1.0
// buildTime: null,
// opaqueFlagBuildTime: null,
// insertTime: null,
};
// ----------------------------------------------------------------------------
function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
// Inheritance
vtkScalarsToColors.extend(publicAPI, model, initialValues);
// Internal objects initialization
if (!model.table) {
model.table = [];
}
model.buildTime = {};
macro.obj(model.buildTime);
model.opaqueFlagBuildTime = {};
macro.obj(model.opaqueFlagBuildTime, {
mtime: 0
});
model.insertTime = {};
macro.obj(model.insertTime, {
mtime: 0
});
// Create get-only macros
macro.get(publicAPI, model, ['buildTime']);
// Create get-set macros
macro.setGet(publicAPI, model, ['numberOfColors', 'useAboveRangeColor', 'useBelowRangeColor']);
// Create set macros for array (needs to know size)
macro.setArray(publicAPI, model, ['alphaRange', 'hueRange', 'saturationRange', 'valueRange'], 2);
macro.setArray(publicAPI, model, ['nanColor', 'belowRangeColor', 'aboveRangeColor'], 4);
// Create get macros for array
macro.getArray(publicAPI, model, ['hueRange', 'saturationRange', 'valueRange', 'alphaRange', 'nanColor', 'belowRangeColor', 'aboveRangeColor']);
// For more macro methods, see "Sources/macros.js"
// Object specific methods
vtkLookupTable(publicAPI, model);
}
// ----------------------------------------------------------------------------
const newInstance = macro.newInstance(extend, 'vtkLookupTable');
// ----------------------------------------------------------------------------
var vtkLookupTable$1 = {
newInstance,
extend
};
export { vtkLookupTable$1 as default, extend, newInstance };