UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

516 lines (411 loc) 19.3 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import macro from '../../macro.js'; import vtkAbstractMapper3D from './AbstractMapper3D.js'; import vtkDataArray from '../../Common/Core/DataArray.js'; import vtkImageData from '../../Common/DataModel/ImageData.js'; import vtkLookupTable from '../../Common/Core/LookupTable.js'; import { M as createUninitializedBounds, i as isNan } from '../../Common/Core/Math/index.js'; import vtkScalarsToColors from '../../Common/Core/ScalarsToColors/Constants.js'; import CoincidentTopologyHelper from './Mapper/CoincidentTopologyHelper.js'; import Constants from './Mapper/Constants.js'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } var staticOffsetAPI = CoincidentTopologyHelper.staticOffsetAPI, otherStaticMethods = CoincidentTopologyHelper.otherStaticMethods; var ColorMode = Constants.ColorMode, ScalarMode = Constants.ScalarMode, GetArray = Constants.GetArray; var VectorMode = vtkScalarsToColors.VectorMode; var VtkDataTypes = vtkDataArray.VtkDataTypes; // ---------------------------------------------------------------------------- function notImplemented(method) { return function () { return macro.vtkErrorMacro("vtkMapper::".concat(method, " - NOT IMPLEMENTED")); }; } // ---------------------------------------------------------------------------- // vtkMapper methods // ---------------------------------------------------------------------------- function vtkMapper(publicAPI, model) { // Set our className model.classHierarchy.push('vtkMapper'); publicAPI.getBounds = function () { var input = publicAPI.getInputData(); if (!input) { model.bounds = createUninitializedBounds(); } else { if (!model.static) { publicAPI.update(); } model.bounds = input.getBounds(); } return model.bounds; }; publicAPI.setForceCompileOnly = function (v) { model.forceCompileOnly = v; // make sure we do NOT call modified() }; publicAPI.createDefaultLookupTable = function () { model.lookupTable = vtkLookupTable.newInstance(); }; publicAPI.getColorModeAsString = function () { return macro.enumToString(ColorMode, model.colorMode); }; publicAPI.setColorModeToDefault = function () { return publicAPI.setColorMode(0); }; publicAPI.setColorModeToMapScalars = function () { return publicAPI.setColorMode(1); }; publicAPI.setColorModeToDirectScalars = function () { return publicAPI.setColorMode(2); }; publicAPI.getScalarModeAsString = function () { return macro.enumToString(ScalarMode, model.scalarMode); }; publicAPI.setScalarModeToDefault = function () { return publicAPI.setScalarMode(0); }; publicAPI.setScalarModeToUsePointData = function () { return publicAPI.setScalarMode(1); }; publicAPI.setScalarModeToUseCellData = function () { return publicAPI.setScalarMode(2); }; publicAPI.setScalarModeToUsePointFieldData = function () { return publicAPI.setScalarMode(3); }; publicAPI.setScalarModeToUseCellFieldData = function () { return publicAPI.setScalarMode(4); }; publicAPI.setScalarModeToUseFieldData = function () { return publicAPI.setScalarMode(5); }; publicAPI.getAbstractScalars = function (input, scalarMode, arrayAccessMode, arrayId, arrayName) { // make sure we have an input if (!input || !model.scalarVisibility) { return { scalars: null, cellFLag: false }; } var scalars = null; var cellFlag = false; // get and scalar data according to scalar mode if (scalarMode === ScalarMode.DEFAULT) { scalars = input.getPointData().getScalars(); if (!scalars) { scalars = input.getCellData().getScalars(); cellFlag = true; } } else if (scalarMode === ScalarMode.USE_POINT_DATA) { scalars = input.getPointData().getScalars(); } else if (scalarMode === ScalarMode.USE_CELL_DATA) { scalars = input.getCellData().getScalars(); cellFlag = true; } else if (scalarMode === ScalarMode.USE_POINT_FIELD_DATA) { var pd = input.getPointData(); if (arrayAccessMode === GetArray.BY_ID) { scalars = pd.getArrayByIndex(arrayId); } else { scalars = pd.getArrayByName(arrayName); } } else if (scalarMode === ScalarMode.USE_CELL_FIELD_DATA) { var cd = input.getCellData(); cellFlag = true; if (arrayAccessMode === GetArray.BY_ID) { scalars = cd.getArrayByIndex(arrayId); } else { scalars = cd.getArrayByName(arrayName); } } else if (scalarMode === ScalarMode.USE_FIELD_DATA) { var fd = input.getFieldData(); if (arrayAccessMode === GetArray.BY_ID) { scalars = fd.getArrayByIndex(arrayId); } else { scalars = fd.getArrayByName(arrayName); } } return { scalars: scalars, cellFlag: cellFlag }; }; publicAPI.mapScalars = function (input, alpha) { var scalars = publicAPI.getAbstractScalars(input, model.scalarMode, model.arrayAccessMode, model.arrayId, model.colorByArrayName).scalars; if (!scalars) { model.colorCoordinates = null; model.colorTextureMap = null; model.colorMapColors = null; return; } // we want to only recompute when something has changed var toString = "".concat(publicAPI.getMTime()).concat(scalars.getMTime()).concat(alpha); if (model.colorBuildString === toString) return; if (!model.useLookupTableScalarRange) { publicAPI.getLookupTable().setRange(model.scalarRange[0], model.scalarRange[1]); } // Decide between texture color or vertex color. // Cell data always uses vertex color. // Only point data can use both texture and vertex coloring. if (publicAPI.canUseTextureMapForColoring(input)) { publicAPI.mapScalarsToTexture(scalars, alpha); } else { model.colorCoordinates = null; model.colorTextureMap = null; var lut = publicAPI.getLookupTable(); if (lut) { // Ensure that the lookup table is built lut.build(); model.colorMapColors = lut.mapScalars(scalars, model.colorMode, -1); } } model.colorBuildString = "".concat(publicAPI.getMTime()).concat(scalars.getMTime()).concat(alpha); }; //----------------------------------------------------------------------------- publicAPI.scalarToTextureCoordinate = function (scalarValue, // Input scalar rangeMin, // range[0] invRangeWidth) { // 1/(range[1]-range[0]) var texCoordS = 0.5; // Scalar value is arbitrary when NaN var texCoordT = 1.0; // 1.0 in t coordinate means NaN if (!isNan(scalarValue)) { // 0.0 in t coordinate means not NaN. So why am I setting it to 0.49? // Because when you are mapping scalars and you have a NaN adjacent to // anything else, the interpolation everywhere should be NaN. Thus, I // want the NaN color everywhere except right on the non-NaN neighbors. // To simulate this, I set the t coord for the real numbers close to // the threshold so that the interpolation almost immediately looks up // the NaN value. texCoordT = 0.49; texCoordS = (scalarValue - rangeMin) * invRangeWidth; // Some implementations apparently don't handle relatively large // numbers (compared to the range [0.0, 1.0]) very well. In fact, // values above 1122.0f appear to cause texture wrap-around on // some systems even when edge clamping is enabled. Why 1122.0f? I // don't know. For safety, we'll clamp at +/- 1000. This will // result in incorrect images when the texture value should be // above or below 1000, but I don't have a better solution. if (texCoordS > 1000.0) { texCoordS = 1000.0; } else if (texCoordS < -1000.0) { texCoordS = -1000.0; } } return { texCoordS: texCoordS, texCoordT: texCoordT }; }; //----------------------------------------------------------------------------- publicAPI.createColorTextureCoordinates = function (input, output, numScalars, numComps, component, range, tableRange, tableNumberOfColors, useLogScale) { // We have to change the range used for computing texture // coordinates slightly to accommodate the special above- and // below-range colors that are the first and last texels, // respectively. var scalarTexelWidth = (range[1] - range[0]) / tableNumberOfColors; var paddedRange = []; paddedRange[0] = range[0] - scalarTexelWidth; paddedRange[1] = range[1] + scalarTexelWidth; var invRangeWidth = 1.0 / (paddedRange[1] - paddedRange[0]); var outputV = output.getData(); var inputV = input.getData(); var count = 0; var outputCount = 0; if (component < 0 || component >= numComps) { for (var scalarIdx = 0; scalarIdx < numScalars; ++scalarIdx) { var sum = 0; for (var compIdx = 0; compIdx < numComps; ++compIdx) { sum += inputV[count] * inputV[count]; count++; } var magnitude = Math.sqrt(sum); if (useLogScale) { magnitude = vtkLookupTable.applyLogScale(magnitude, tableRange, range); } var outputs = publicAPI.scalarToTextureCoordinate(magnitude, paddedRange[0], invRangeWidth); outputV[outputCount] = outputs.texCoordS; outputV[outputCount + 1] = outputs.texCoordT; outputCount += 2; } } else { count += component; for (var _scalarIdx = 0; _scalarIdx < numScalars; ++_scalarIdx) { var inputValue = inputV[count]; if (useLogScale) { inputValue = vtkLookupTable.applyLogScale(inputValue, tableRange, range); } var _outputs = publicAPI.scalarToTextureCoordinate(inputValue, paddedRange[0], invRangeWidth); outputV[outputCount] = _outputs.texCoordS; outputV[outputCount + 1] = _outputs.texCoordT; outputCount += 2; count += numComps; } } }; publicAPI.mapScalarsToTexture = function (scalars, alpha) { var range = model.lookupTable.getRange(); var useLogScale = model.lookupTable.usingLogScale(); if (useLogScale) { // convert range to log. vtkLookupTable.getLogRange(range, range); } var origAlpha = model.lookupTable.getAlpha(); // Get rid of vertex color array. Only texture or vertex coloring // can be active at one time. The existence of the array is the // signal to use that technique. model.colorMapColors = null; // If the lookup table has changed, then recreate the color texture map. // Set a new lookup table changes this->MTime. if (model.colorTextureMap == null || publicAPI.getMTime() > model.colorTextureMap.getMTime() || model.lookupTable.getMTime() > model.colorTextureMap.getMTime() || model.lookupTable.getAlpha() !== alpha) { model.lookupTable.setAlpha(alpha); model.colorTextureMap = null; // Get the texture map from the lookup table. // Create a dummy ramp of scalars. // In the future, we could extend vtkScalarsToColors. model.lookupTable.build(); var numberOfColors = model.lookupTable.getNumberOfAvailableColors(); if (numberOfColors > 4094) { numberOfColors = 4094; } numberOfColors += 2; var k = (range[1] - range[0]) / (numberOfColors - 1 - 2); var newArray = new Float64Array(numberOfColors * 2); for (var i = 0; i < numberOfColors; ++i) { newArray[i] = range[0] + i * k - k; // minus k to start at below range color if (useLogScale) { newArray[i] = Math.pow(10.0, newArray[i]); } } // Dimension on NaN. for (var _i = 0; _i < numberOfColors; ++_i) { newArray[_i + numberOfColors] = NaN; } model.colorTextureMap = vtkImageData.newInstance(); model.colorTextureMap.setExtent(0, numberOfColors - 1, 0, 1, 0, 0); var tmp = vtkDataArray.newInstance({ numberOfComponents: 1, values: newArray }); model.colorTextureMap.getPointData().setScalars(model.lookupTable.mapScalars(tmp, model.colorMode, 0)); model.lookupTable.setAlpha(origAlpha); } // Create new coordinates if necessary. // Need to compare lookup table in case the range has changed. if (!model.colorCoordinates || publicAPI.getMTime() > model.colorCoordinates.getMTime() || publicAPI.getInputData(0).getMTime() > model.colorCoordinates.getMTime() || model.lookupTable.getMTime() > model.colorCoordinates.getMTime()) { // Get rid of old colors model.colorCoordinates = null; // Now create the color texture coordinates. var numComps = scalars.getNumberOfComponents(); var num = scalars.getNumberOfTuples(); // const fArray = new FloatArray(num * 2); model.colorCoordinates = vtkDataArray.newInstance({ numberOfComponents: 2, values: new Float32Array(num * 2) }); var scalarComponent = model.lookupTable.getVectorComponent(); // Although I like the feature of applying magnitude to single component // scalars, it is not how the old MapScalars for vertex coloring works. if (model.lookupTable.getVectorMode() === VectorMode.MAGNITUDE && scalars.getNumberOfComponents() > 1) { scalarComponent = -1; } publicAPI.createColorTextureCoordinates(scalars, model.colorCoordinates, num, numComps, scalarComponent, range, model.lookupTable.getRange(), model.colorTextureMap.getPointData().getScalars().getNumberOfTuples() / 2 - 2, useLogScale); } }; publicAPI.getIsOpaque = function () { var lut = publicAPI.getLookupTable(); if (lut) { // Ensure that the lookup table is built lut.build(); return lut.isOpaque(); } return true; }; publicAPI.canUseTextureMapForColoring = function (input) { if (!model.interpolateScalarsBeforeMapping) { return false; // user doesn't want us to use texture maps at all. } // index color does not use textures if (model.lookupTable && model.lookupTable.getIndexedLookup()) { return false; } var gasResult = publicAPI.getAbstractScalars(input, model.scalarMode, model.arrayAccessMode, model.arrayId, model.colorByArrayName); var scalars = gasResult.scalars; if (!scalars) { // no scalars on this dataset, we don't care if texture is used at all. return false; } if (gasResult.cellFlag) { return false; // cell data colors, don't use textures. } if (model.colorMode === ColorMode.DEFAULT && scalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR || model.colorMode === ColorMode.DIRECT_SCALARS) { // Don't use texture is direct coloring using RGB unsigned chars is // requested. return false; } return true; }; publicAPI.clearColorArrays = function () { model.colorMapColors = null; model.colorCoordinates = null; model.colorTextureMap = null; }; publicAPI.getLookupTable = function () { if (!model.lookupTable) { publicAPI.createDefaultLookupTable(); } return model.lookupTable; }; publicAPI.getMTime = function () { var mt = model.mtime; if (model.lookupTable !== null) { var time = model.lookupTable.getMTime(); mt = time > mt ? time : mt; } return mt; }; publicAPI.getPrimitiveCount = function () { var input = publicAPI.getInputData(); var pcount = { points: input.getPoints().getNumberOfValues() / 3, verts: input.getVerts().getNumberOfValues() - input.getVerts().getNumberOfCells(), lines: input.getLines().getNumberOfValues() - 2 * input.getLines().getNumberOfCells(), triangles: input.getPolys().getNumberOfValues() - 3 * input.getLines().getNumberOfCells() }; return pcount; }; publicAPI.acquireInvertibleLookupTable = notImplemented('AcquireInvertibleLookupTable'); publicAPI.valueToColor = notImplemented('ValueToColor'); publicAPI.colorToValue = notImplemented('ColorToValue'); publicAPI.useInvertibleColorFor = notImplemented('UseInvertibleColorFor'); publicAPI.clearInvertibleColor = notImplemented('ClearInvertibleColor'); } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { colorMapColors: null, // Same as this->Colors static: false, lookupTable: null, scalarVisibility: true, scalarRange: [0, 1], useLookupTableScalarRange: false, colorMode: 0, scalarMode: 0, arrayAccessMode: 1, // By_NAME renderTime: 0, colorByArrayName: null, fieldDataTupleId: -1, interpolateScalarsBeforeMapping: false, colorCoordinates: null, colorTextureMap: null, forceCompileOnly: 0, useInvertibleColors: false, invertibleScalars: null, viewSpecificProperties: null, customShaderAttributes: [] }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkAbstractMapper3D.extend(publicAPI, model, initialValues); macro.get(publicAPI, model, ['colorCoordinates', 'colorMapColors', 'colorTextureMap']); macro.setGet(publicAPI, model, ['colorByArrayName', 'arrayAccessMode', 'colorMode', 'fieldDataTupleId', 'interpolateScalarsBeforeMapping', 'lookupTable', 'renderTime', 'scalarMode', 'scalarVisibility', 'static', 'useLookupTableScalarRange', 'viewSpecificProperties', 'customShaderAttributes' // point data array names that will be transferred to the VBO ]); macro.setGetArray(publicAPI, model, ['scalarRange'], 2); if (!model.viewSpecificProperties) { model.viewSpecificProperties = {}; } CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model); // Object methods vtkMapper(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkMapper'); // ---------------------------------------------------------------------------- var vtkMapper$1 = _objectSpread(_objectSpread(_objectSpread({ newInstance: newInstance, extend: extend }, staticOffsetAPI), otherStaticMethods), Constants); export default vtkMapper$1; export { extend, newInstance };