UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

515 lines (469 loc) 17.9 kB
import { m as macro } from '../../macros2.js'; import vtkDataArray from './DataArray.js'; import vtkScalarsToColors$2 from './ScalarsToColors/Constants.js'; import Constants from '../../Rendering/Core/Mapper/Constants.js'; const { ScalarMappingTarget, VectorMode } = vtkScalarsToColors$2; const { VtkDataTypes } = vtkDataArray; const { ColorMode } = Constants; const { vtkErrorMacro } = macro; // ---------------------------------------------------------------------------- // Global methods // ---------------------------------------------------------------------------- // Add module-level functions or api that you want to expose statically via // the next section... // ---------------------------------------------------------------------------- // Static API // ---------------------------------------------------------------------------- function intColorToUChar(c) { return c; } function floatColorToUChar(c) { return Math.floor(c * 255.0 + 0.5); } // ---------------------------------------------------------------------------- // vtkScalarsToColors methods // ---------------------------------------------------------------------------- function vtkScalarsToColors(publicAPI, model) { // Set our className model.classHierarchy.push('vtkScalarsToColors'); publicAPI.setVectorModeToMagnitude = () => publicAPI.setVectorMode(VectorMode.MAGNITUDE); publicAPI.setVectorModeToComponent = () => publicAPI.setVectorMode(VectorMode.COMPONENT); publicAPI.setVectorModeToRGBColors = () => publicAPI.setVectorMode(VectorMode.RGBCOLORS); publicAPI.build = () => {}; publicAPI.isOpaque = () => true; //---------------------------------------------------------------------------- publicAPI.setAnnotations = (values, annotations) => { if (values && !annotations || !values && annotations) { return; } if (values && annotations && values.length !== annotations.length) { vtkErrorMacro('Values and annotations do not have the same number of tuples so ignoring'); return; } model.annotationArray = []; if (annotations && values) { const num = annotations.length; for (let i = 0; i < num; i++) { model.annotationArray.push({ value: values[i], annotation: String(annotations[i]) }); } } publicAPI.updateAnnotatedValueMap(); publicAPI.modified(); }; //---------------------------------------------------------------------------- publicAPI.setAnnotation = (value, annotation) => { let i = publicAPI.checkForAnnotatedValue(value); let modified = false; if (i >= 0) { if (model.annotationArray[i].annotation !== annotation) { model.annotationArray[i].annotation = annotation; modified = true; } } else { model.annotationArray.push({ value, annotation }); i = model.annotationArray.length - 1; modified = true; } if (modified) { publicAPI.updateAnnotatedValueMap(); publicAPI.modified(); } return i; }; //---------------------------------------------------------------------------- publicAPI.getNumberOfAnnotatedValues = () => model.annotationArray.length; //---------------------------------------------------------------------------- publicAPI.getAnnotatedValue = idx => { if (idx < 0 || idx >= model.annotationArray.length) { return null; } return model.annotationArray[idx].value; }; //---------------------------------------------------------------------------- publicAPI.getAnnotation = idx => { if (model.annotationArray[idx] === undefined) { return null; } return model.annotationArray[idx].annotation; }; //---------------------------------------------------------------------------- publicAPI.getAnnotatedValueIndex = val => model.annotationArray.length ? publicAPI.checkForAnnotatedValue(val) : -1; //---------------------------------------------------------------------------- publicAPI.removeAnnotation = value => { const i = publicAPI.checkForAnnotatedValue(value); const needToRemove = i >= 0; if (needToRemove) { model.annotationArray.splice(i, 1); publicAPI.updateAnnotatedValueMap(); publicAPI.modified(); } return needToRemove; }; //---------------------------------------------------------------------------- publicAPI.resetAnnotations = () => { model.annotationArray = []; model.annotatedValueMap = []; publicAPI.modified(); }; //---------------------------------------------------------------------------- publicAPI.getAnnotationColor = (val, rgba) => { if (model.indexedLookup) { const i = publicAPI.getAnnotatedValueIndex(val); publicAPI.getIndexedColor(i, rgba); } else { publicAPI.getColor(parseFloat(val), rgba); rgba[3] = 1.0; } }; //---------------------------------------------------------------------------- publicAPI.checkForAnnotatedValue = value => publicAPI.getAnnotatedValueIndexInternal(value); //---------------------------------------------------------------------------- // An unsafe version of vtkScalarsToColors::CheckForAnnotatedValue for // internal use (no pointer checks performed) publicAPI.getAnnotatedValueIndexInternal = value => { if (model.annotatedValueMap[value] !== undefined) { const na = model.annotationArray.length; return model.annotatedValueMap[value] % na; } // Treat as a NaN return -1; }; //---------------------------------------------------------------------------- publicAPI.getIndexedColor = (val, rgba) => { rgba[0] = 0.0; rgba[1] = 0.0; rgba[2] = 0.0; rgba[3] = 0.0; }; //---------------------------------------------------------------------------- publicAPI.updateAnnotatedValueMap = () => { model.annotatedValueMap = []; const na = model.annotationArray.length; for (let i = 0; i < na; i++) { model.annotatedValueMap[model.annotationArray[i].value] = i; } }; // Description: // Internal methods that map a data array into a 4-component, // unsigned char RGBA array. The color mode determines the behavior // of mapping. If ColorMode.DEFAULT is set, then unsigned char // data arrays are treated as colors (and converted to RGBA if // necessary); If ColorMode.DIRECT_SCALARS is set, then all arrays // are treated as colors (integer types are clamped in the range 0-255, // floating point arrays are clamped in the range 0.0-1.0. Note 'char' does // not have enough values to represent a color so mapping this type is // considered an error); // otherwise, the data is mapped through this instance // of ScalarsToColors. The component argument is used for data // arrays with more than one component; it indicates which component // to use to do the blending. When the component argument is -1, // then the this object uses its own selected technique to change a // vector into a scalar to map. publicAPI.mapScalars = (scalars, colorMode, componentIn) => { const numberOfComponents = scalars.getNumberOfComponents(); let newColors = null; // map scalars through lookup table only if needed if (colorMode === ColorMode.DEFAULT && (scalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR || scalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR_CLAMPED) || colorMode === ColorMode.DIRECT_SCALARS && scalars) { newColors = publicAPI.convertToRGBA(scalars, numberOfComponents, scalars.getNumberOfTuples()); } else { const newscalars = { type: 'vtkDataArray', name: 'temp', numberOfComponents: 4, dataType: VtkDataTypes.UNSIGNED_CHAR }; const s = macro.newTypedArray(newscalars.dataType, 4 * scalars.getNumberOfTuples()); newscalars.values = s; newscalars.size = s.length; newColors = vtkDataArray.newInstance(newscalars); let component = componentIn; // If mapper did not specify a component, use the VectorMode if (component < 0 && numberOfComponents > 1) { publicAPI.mapVectorsThroughTable(scalars, newColors, ScalarMappingTarget.RGBA, -1, -1); } else { if (component < 0) { component = 0; } if (component >= numberOfComponents) { component = numberOfComponents - 1; } // Map the scalars to colors publicAPI.mapScalarsThroughTable(scalars, newColors, ScalarMappingTarget.RGBA, component); } } return newColors; }; publicAPI.mapVectorsToMagnitude = (input, output, compsToUse) => { const length = input.getNumberOfTuples(); const inIncr = input.getNumberOfComponents(); const outputV = output.getData(); const inputV = input.getData(); for (let i = 0; i < length; i++) { let sum = 0.0; for (let j = 0; j < compsToUse; j++) { sum += inputV[i * inIncr + j] * inputV[i * inIncr + j]; } outputV[i] = Math.sqrt(sum); } }; //---------------------------------------------------------------------------- // Map a set of vector values through the table publicAPI.mapVectorsThroughTable = (input, output, outputFormat, vectorComponentIn, vectorSizeIn) => { let vectorMode = publicAPI.getVectorMode(); let vectorSize = vectorSizeIn; let vectorComponent = vectorComponentIn; const inComponents = input.getNumberOfComponents(); if (vectorMode === VectorMode.COMPONENT) { // make sure vectorComponent is within allowed range if (vectorComponent === -1) { // if set to -1, use default value provided by table vectorComponent = publicAPI.getVectorComponent(); } if (vectorComponent < 0) { vectorComponent = 0; } if (vectorComponent >= inComponents) { vectorComponent = inComponents - 1; } } else { // make sure vectorSize is within allowed range if (vectorSize === -1) { // if set to -1, use default value provided by table vectorSize = publicAPI.getVectorSize(); } if (vectorSize <= 0) { vectorComponent = 0; vectorSize = inComponents; } else { if (vectorComponent < 0) { vectorComponent = 0; } if (vectorComponent >= inComponents) { vectorComponent = inComponents - 1; } if (vectorComponent + vectorSize > inComponents) { vectorSize = inComponents - vectorComponent; } } if (vectorMode === VectorMode.MAGNITUDE && (inComponents === 1 || vectorSize === 1)) { vectorMode = VectorMode.COMPONENT; } } // increment input pointer to the first component to map let inputOffset = 0; if (vectorComponent > 0) { inputOffset = vectorComponent; } // map according to the current vector mode switch (vectorMode) { case VectorMode.COMPONENT: { publicAPI.mapScalarsThroughTable(input, output, outputFormat, inputOffset); break; } case VectorMode.RGBCOLORS: { // publicAPI.mapColorsToColors( // input, output, inComponents, vectorSize, // outputFormat); break; } // MAGNITUDE is considered default case VectorMode.MAGNITUDE: default: { const magValues = vtkDataArray.newInstance({ numberOfComponents: 1, values: new Float32Array(input.getNumberOfTuples()) }); publicAPI.mapVectorsToMagnitude(input, magValues, vectorSize); publicAPI.mapScalarsThroughTable(magValues, output, outputFormat, 0); break; } } }; publicAPI.luminanceToRGBA = (newColors, colors, alpha, convtFun) => { const a = convtFun(alpha); const values = colors.getData(); const newValues = newColors.getData(); const size = values.length; const component = 0; const tuple = 1; let count = 0; for (let i = component; i < size; i += tuple) { const l = convtFun(values[i]); newValues[count * 4] = l; newValues[count * 4 + 1] = l; newValues[count * 4 + 2] = l; newValues[count * 4 + 3] = a; count++; } }; publicAPI.luminanceAlphaToRGBA = (newColors, colors, alpha, convtFun) => { const values = colors.getData(); const newValues = newColors.getData(); const size = values.length; const component = 0; const tuple = 2; let count = 0; for (let i = component; i < size; i += tuple) { const l = convtFun(values[i]); newValues[count] = l; newValues[count + 1] = l; newValues[count + 2] = l; newValues[count + 3] = convtFun(values[i + 1]) * alpha; count += 4; } }; publicAPI.rGBToRGBA = (newColors, colors, alpha, convtFun) => { const a = floatColorToUChar(alpha); const values = colors.getData(); const newValues = newColors.getData(); const size = values.length; const component = 0; const tuple = 3; let count = 0; for (let i = component; i < size; i += tuple) { newValues[count * 4] = convtFun(values[i]); newValues[count * 4 + 1] = convtFun(values[i + 1]); newValues[count * 4 + 2] = convtFun(values[i + 2]); newValues[count * 4 + 3] = a; count++; } }; publicAPI.rGBAToRGBA = (newColors, colors, alpha, convtFun) => { const values = colors.getData(); const newValues = newColors.getData(); const size = values.length; const component = 0; const tuple = 4; let count = 0; for (let i = component; i < size; i += tuple) { newValues[count * 4] = convtFun(values[i]); newValues[count * 4 + 1] = convtFun(values[i + 1]); newValues[count * 4 + 2] = convtFun(values[i + 2]); newValues[count * 4 + 3] = convtFun(values[i + 3]) * alpha; count++; } }; //---------------------------------------------------------------------------- publicAPI.convertToRGBA = (colors, numComp, numTuples) => { let { alpha } = model; if (numComp === 4 && alpha >= 1.0 && colors.getDataType() === VtkDataTypes.UNSIGNED_CHAR) { return colors; } const newColors = vtkDataArray.newInstance({ numberOfComponents: 4, empty: true, size: 4 * numTuples, dataType: VtkDataTypes.UNSIGNED_CHAR }); if (numTuples <= 0) { return newColors; } alpha = alpha > 0 ? alpha : 0; alpha = alpha < 1 ? alpha : 1; let convtFun = intColorToUChar; if (colors.getDataType() === VtkDataTypes.FLOAT || colors.getDataType() === VtkDataTypes.DOUBLE) { convtFun = floatColorToUChar; } switch (numComp) { case 1: publicAPI.luminanceToRGBA(newColors, colors, alpha, convtFun); break; case 2: publicAPI.luminanceAlphaToRGBA(newColors, colors, convtFun); break; case 3: publicAPI.rGBToRGBA(newColors, colors, alpha, convtFun); break; case 4: publicAPI.rGBAToRGBA(newColors, colors, alpha, convtFun); break; default: vtkErrorMacro('Cannot convert colors'); return null; } return newColors; }; publicAPI.usingLogScale = () => false; publicAPI.getNumberOfAvailableColors = () => 256 * 256 * 256; publicAPI.setRange = (min, max) => publicAPI.setMappingRange(min, max); publicAPI.getRange = () => publicAPI.getMappingRange(); publicAPI.areScalarsOpaque = (scalars, colorMode, componentIn) => { if (!scalars) { return publicAPI.isOpaque(); } const numberOfComponents = scalars.getNumberOfComponents(); // map scalars through lookup table only if needed if (colorMode === ColorMode.DEFAULT && scalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR || colorMode === ColorMode.DIRECT_SCALARS) { // we will be using the scalars directly, so look at the number of // components and the range if (numberOfComponents === 3 || numberOfComponents === 1) { return model.alpha >= 1.0; } // otherwise look at the range of the alpha channel const range = scalars.getRange(numberOfComponents - 1); return range[0] === 255; } return true; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { alpha: 1.0, vectorComponent: 0, vectorSize: -1, vectorMode: VectorMode.COMPONENT, mappingRange: null, annotationArray: null, annotatedValueMap: null, indexedLookup: false }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Object methods macro.obj(publicAPI, model); model.mappingRange = [0, 255]; model.annotationArray = []; model.annotatedValueMap = []; // Create get-set macros macro.setGet(publicAPI, model, ['vectorSize', 'vectorComponent', 'vectorMode', 'alpha', 'indexedLookup']); // Create set macros for array (needs to know size) macro.setArray(publicAPI, model, ['mappingRange'], 2); // Create get macros for array macro.getArray(publicAPI, model, ['mappingRange']); // For more macro methods, see "Sources/macros.js" // Object specific methods vtkScalarsToColors(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkScalarsToColors'); // ---------------------------------------------------------------------------- var vtkScalarsToColors$1 = { newInstance, extend, ...vtkScalarsToColors$2 }; export { vtkScalarsToColors$1 as default, extend, newInstance };